請問如何是否能解決這個程式要多個View去共享一個ViewModel的辦法?
目前這個程式的View架構如下:
MainWindow.xaml (window)
|__ReviewView.xaml (page)
|__DisplayControlView.xaml (page)
|__DisplayPanelView.xaml (page)
|__DisplayView.xaml (page)
ReviewView.xaml用frame容器包含DisplayControlView.xaml跟DisplayPanelView.xaml兩個Page
DisplayPanelView.xaml又往下包含了一個DisplayView.xaml
主要功能是-在DisplayControlView上有按鈕可以開啟圖檔,然後顯示在DisplayView上
目前找過文章說可以View設定DataContext綁定ViewModel,或是反過來,但是都沒很完整的範例示範,還請各位大大能幫幫忙!謝謝!
我試過用Singleton修改過,結果越改越糟糕........囧
以下是我原始的程式片段:
--DisplayControlView.xaml
<Page.DataContext>
<vm:DisplayViewModel/>
</Page.DataContext>
...
<Button x:Name="btnDpOpen" Style="{DynamicResource ButtonStyle}" Width="120" Command="{Binding OpenFile}">
...
--DisplayView.xaml
<Page.DataContext>
<vm:DisplayViewModel/>
</Page.DataContext>
...
<Image x:Name="imgDisplayView" Source="{Binding ImagePath}">
<Image.RenderTransform>
<TransformGroup>
<TranslateTransform/>
<RotateTransform/>
<ScaleTransform/>
</TransformGroup>
</Image.RenderTransform>
</Image>
...
--DisplayViewModel.cs
class DisplayViewModel : ViewModelBase
{
private DisplayImageModel image { get; set; }
private ObservableCollection<DisplayImageModel> imagelist = new ObservableCollection<DisplayImageModel>();
public ObservableCollection<DisplayImageModel> ImageList
{
get { return imagelist; }
set
{
imagelist = value;
OnPropertyChanged();
}
}
public string ImagePath
{
get { return image.Path; }
set
{
if (image.Path != value)
{
image.Path = value;
OnPropertyChanged();
}
}
}
public DisplayViewModel()
{
image = new DisplayImageModel();
ImagePath = @"C:\lenna.png";
}
public bool CanExecute()
{
return true;
}
public RelayCommand OpenFile
{
get { return new RelayCommand(openFile, CanExecute); }
}
private void openFile()
{
string[] picExtName = new string[] { ".PNG", ".JPG", "JEPG", "BMP" };
OpenFileDialog dlgOpenFile = new OpenFileDialog()
{ Filter = "Picture|*.jpg;*.jpeg;*.bmp;*.png|All File|*.*" };
if (dlgOpenFile.ShowDialog() != true)
{
return;
}
if (picExtName.Any(System.IO.Path.GetExtension(dlgOpenFile.FileName).ToUpper().Contains))
{
ImagePath = dlgOpenFile.FileName;
OnPropertyChanged();
}
}
}
--ViewModelBase.cs
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void SetValue<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
if (property != null)
{
if (property.Equals(value))
{
return;
}
}
property = value;
OnPropertyChanged(propertyName);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
--RelayCommond.cs
public class RelayCommand : ICommand
{
readonly Func<bool> _canExecute;
readonly Action _execute;
public RelayCommand(Action execute) : this(execute, null)
{
}
public RelayCommand(Action execute, Func<bool> canExecute)
{
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null) CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null) CommandManager.RequerySuggested += value;
}
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(object parameter)
{
_execute();
}
}
Xaml? WPF喔.
多個View共用一個ViewModel,
有甚麼問題嗎?
先宣告
private DisplayViewModel viewModel = new DisplayViewModel();
在建構子的地方加入,
DataContext = viewModel;
但是你的DisplayViewModel沒有宣告public可能無法共用.
DisplayControlView開啟的影像無法在DisplayView顯示出來
還是有可以提供的關鍵字或範例參考?
我理解是缺省public private protected時,應該是設定public的
我是有嘗試在建構子裡故意給一個影像路徑預設值
發現都會顯示預設的這個影像,所以猜是開的檔案路徑字串沒正確傳給另一個View,應該Notify也只作用在自己的View上
只是要改成同一個instance,沒什麼頭緒
其實不是很懂這需求,
你要直接顯示看要不要寫使用者控制項包在裡面,
或是把影像檔案記下來,
進入DisplayView再打開.
我嘗試全寫在一起這MVVM模式是正確的,但實際上需要模組化才能重複應用DisplayView才分開的
正常使用上也不太是只開一個影像檔,除非是交學校作業 XD
網上搜到是還有用Event傳遞,但我的DisplayView跟DisplayControlView並不在Logical Tree/Visual Tree的直接上下關係,就比較麻煩
對了,大大的辦法不就等同在xaml內的
<Page.DataContext>
<vm:DisplayViewModel/>
</Page.DataContext>
只差在寫在.cs或是.xaml而已吧?
另外也要『正確』觸發OnPropertyChanged去通知UI更新才行
這也是要解的問題啊!
那兩個DisplayViewModel其實是不同一個,
或許你要考慮的是傳遞影片字串,
看是要用一個共用的static參數或是使用參數傳遞的方式.
另外你更新UI會有問題嗎?
大大說的方法有範例嗎?看大大是WPF大師呢,應該MVVM也熟的~^^
基本上,上述問題中就說instance不是同一個,所以兩個View各自為政
嘗試Singleton Pattern改過,DisplayView也沒成功收到字串
也試過在兩者的根結點上的ReviewView去設定過DataContext也是無法把字串傳過去,DisplayView的影像還是出不來
所以想問說,MVVM下如何將ViewModel實例化instance是固定一個給數個View使用的
如果方法不拘的話,
WinForm跟WPF其實都可以寫共用的類別跟共用的函式,
讀取或修改的時候直接去改它們就可以,
跟你在哪個檔案裡面沒有關係.
如果你一定要用同一個ViewModel的話,
或許在MainWindow新增一個static的ViewModel試試看,
(不用static設成public也是可以,
但是你就需要一直傳遞MainWindow物件)
然後DataContext就不要寫在Xaml了,
就寫在後端吧.
我是沒有這樣子做過,
不過似乎是可行的.
經過幾天嘗試,目前是用Singleton Pattern勉強符合像MVVM的樣子
對Pure MVVM方法還努力中,有些不甚滿意
目標是.xaml.cs內的程式碼清空啊!!!!
####DisplayViewModel.cs
加入Double-Checking Lock SingleTon
改建構子修飾詞限制為private
private static readonly object padlock = new object();
private static volatile DisplayViewModel _instance = null;
public static DisplayViewModel Instance
{
get
{
if (_instance == null)
{
lock (padlock)
{
if (_instance == null)
{
_instance = new DisplayViewModel();
}
}
}
return _instance;
}
}
private static DisplayImageModel image { get; set; }
...
####DisplayView.xaml & DisplayControlView.xaml
刪除以下的DataContext引用
<Page.DataContext>
<vm:DisplayViewModel/>
</Page.DataContext>
####DisplayView.xaml.cs & DisplayControlView.xaml.cs
在初始化元件後設定DataContext為Singleton ViewModel
InitializeComponent();
this.DataContext = DisplayViewModel.Instance;
清空就不用寫了..
可以運作應該就沒問題了.