1 回答

TA貢獻(xiàn)1785條經(jīng)驗(yàn) 獲得超4個(gè)贊
在我看來(lái),你目前的嘗試是在正確的軌道上。您發(fā)布的代碼的主要問(wèn)題是CreateAccountView.Button_Click()
處理程序無(wú)權(quán)訪問(wèn)DataContext
它應(yīng)該設(shè)置的屬性:
private void Button_Click(object sender, RoutedEventArgs e) { DataContext = new MainWindowViewModel(); }
該DataContext
屬性屬于CreateAccountView
用戶控件。但是,這不是所顯示內(nèi)容的控制上下文。因此更改該屬性的值DataContext
不會(huì)產(chǎn)生任何有用的效果。(事實(shí)上,用戶控件DataContext
根本不應(yīng)該設(shè)置自己的屬性,因?yàn)檫@樣做會(huì)丟棄使用該用戶控件的客戶端代碼設(shè)置的任何上下文。)
沒(méi)有足夠的上下文來(lái)確切地知道執(zhí)行此操作的最佳方法是什么。我認(rèn)為在 Stack Overflow 上不可能提供足夠的上下文。整體架構(gòu)將取決于程序的太多小細(xì)節(jié)。但是,我認(rèn)為解決這個(gè)問(wèn)題的一種很好的方法是:
創(chuàng)建一個(gè)“主”視圖模型來(lái)管理應(yīng)用程序的整體行為
創(chuàng)建與 UI 的不同狀態(tài)相關(guān)的單獨(dú)視圖模型
讓主視圖模型配置各個(gè)視圖模型,根據(jù)用戶輸入(例如單擊按鈕)適當(dāng)切換當(dāng)前視圖模型
將其翻譯成代碼,看起來(lái)像這樣......
首先,視圖模型:
class MainViewModel : NotifyPropertyChangedBase
{
private object _currentViewModel;
public object CurrentViewModel
{
get => _currentViewModel;
set => _UpdateField(ref _currentViewModel, value);
}
private readonly HomeViewModel _homeViewModel;
private readonly Sub1ViewModel _sub1ViewModel;
private readonly Sub2ViewModel _sub2ViewModel;
public MainViewModel()
{
_sub1ViewModel = new Sub1ViewModel
{
BackCommand = new DelegateCommand(() => CurrentViewModel = _homeViewModel)
};
_sub2ViewModel = new Sub2ViewModel
{
BackCommand = new DelegateCommand(() => CurrentViewModel = _homeViewModel)
};
_homeViewModel = new HomeViewModel
{
ShowSub1Command = new DelegateCommand(() => CurrentViewModel = _sub1ViewModel),
ShowSub2Command = new DelegateCommand(() => CurrentViewModel = _sub2ViewModel)
};
CurrentViewModel = _homeViewModel;
}
}
class HomeViewModel : NotifyPropertyChangedBase
{
private ICommand _showSub1Command;
public ICommand ShowSub1Command
{
get => _showSub1Command;
set => _UpdateField(ref _showSub1Command, value);
}
private ICommand _showSub2Command;
public ICommand ShowSub2Command
{
get => _showSub2Command;
set => _UpdateField(ref _showSub2Command, value);
}
}
class Sub1ViewModel : NotifyPropertyChangedBase
{
private ICommand _backCommand;
public ICommand BackCommand
{
get => _backCommand;
set => _UpdateField(ref _backCommand, value);
}
}
class Sub2ViewModel : NotifyPropertyChangedBase
{
private ICommand _backCommand;
public ICommand BackCommand
{
get => _backCommand;
set => _UpdateField(ref _backCommand, value);
}
}
當(dāng)然,這些視圖模型只包含處理 UI 切換所需的實(shí)現(xiàn)細(xì)節(jié)。在您的程序中,每個(gè)視圖狀態(tài)還包含您需要的特定于每個(gè)視圖狀態(tài)的內(nèi)容。
在我的小示例中,“主頁(yè)”視圖包含幾個(gè)按鈕,用于選擇可用的各個(gè)子視圖:
<UserControl x:Class="WpfApp1.HomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Home: "/>
<Button Content="Sub1" Command="{Binding ShowSub1Command}"/>
<Button Content="Sub2" Command="{Binding ShowSub2Command}"/>
</StackPanel>
</UserControl>
子視圖僅包含返回主視圖所需的按鈕:
<UserControl x:Class="WpfApp1.Sub1View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Sub1 View: "/>
<Button Content="Back" Command="{Binding BackCommand}"/>
</StackPanel>
</UserControl>
<UserControl x:Class="WpfApp1.Sub2View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Sub2 View: "/>
<Button Content="Back" Command="{Binding BackCommand}"/>
</StackPanel>
</UserControl>
最后,主窗口設(shè)置主視圖模型,并聲明用于每個(gè)特定子視圖的模板:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<l:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type l:HomeViewModel}">
<l:HomeView/>
</DataTemplate>
<DataTemplate DataType="{x:Type l:Sub1ViewModel}">
<l:Sub1View/>
</DataTemplate>
<DataTemplate DataType="{x:Type l:Sub2ViewModel}">
<l:Sub2View/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ContentControl Content="{Binding CurrentViewModel}"/>
</StackPanel>
</Window>
重要的是,您將看到所有視圖對(duì)象都不包含任何隱藏代碼。當(dāng)您以這種方式處理問(wèn)題時(shí),沒(méi)有必要這樣做,至少不是為了控制代碼中的基本行為。(您可能仍然會(huì)遇到視圖對(duì)象的代碼隱藏,但這通常只是為了實(shí)現(xiàn)該視圖對(duì)象特有的特定用戶界面行為,而不是為了處理視圖模型狀態(tài)。)
使用這種方法,您可以讓 WPF 完成盡可能多的繁重工作。它還將所有視圖模型對(duì)象相互解耦。有一個(gè)清晰的層次結(jié)構(gòu):只有頂級(jí)“主”視圖模型才知道其他視圖模型。這允許子視圖模型(“home”、“sub1”和“sub2”)根據(jù)需要在其他場(chǎng)景中重用,而無(wú)需在其中進(jìn)行任何修改或特殊情況處理。
這是我上面使用的輔助類(lèi):
class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class DelegateCommand : ICommand
{
private readonly Action _execute;
public DelegateCommand(Action execute)
{
_execute = execute;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _execute();
}
- 1 回答
- 0 關(guān)注
- 172 瀏覽
添加回答
舉報(bào)