我是 WPF 和 MVVM 新手。我花了两天时间浏览了一堆关于这个主题的问答,但我还没有成功地让它发挥作用。这不可避免地意味着我忽略了一些愚蠢的事情。
我尝试使用
DataTemplate
和 ContentControl
在用户更改 DataGrid
控件中的选项卡时在内容 Fluent:Ribbon
之间切换。我想重用这些视图,因为其中包含的 DataGrid
的填充成本可能很高。
我有这些视图/视图模型:
MainWindow.xaml
/ MainWindowViewModel.cs
- 主应用程序窗口由 Fluent:RibbonWindow
、Fluent:Ribbon
和 Fluent:StatusBar
控件组成(为了清楚起见,在下面的代码片段中删除了其中一些控件)。此类包含用于跟踪“当前内容”(CurrentViewModel
) 的成员属性和用于当用户单击 Fluent:Ribbon
中的按钮时进行命令处理的成员属性。其他视图模型在此类中实例化为私有成员。ProviderView.xaml
/ ProviderViewModel.cs
- 显示“提供商”列表(就本文而言,只是一个抽象概念)。该视图包含一个 UserControl
,其中包含一个 DataGrid
控件。 DataGrid
绑定到 Providers
实例内的公共 ProviderViewModel
属性(Provider 对象的列表),这是 MainWindowViewModel
的公共属性。当我运行应用程序时,在设计器中也很明显,
ContentControl
只包含一个字符串ViewModel.ProviderViewModel
,好像它不知道如何处理该控件,或者我没有正确地树化它。
MainWindow.xaml
<Fluent:RibbonWindow x:Class="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:vm="clr-namespace:ViewModel"
xmlns:local="clr-namespace:MyProgram"
xmlns:Fluent="urn:fluent-ribbon"
mc:Ignorable="d"
Width="800"
Height="600"
Name="MainRibbonWindow"
Icon="{DynamicResource logo}">
<Fluent:RibbonWindow.Resources>
<DataTemplate x:Key="g_ProviderViewModel" DataType="{x:Type vm:ProviderViewModel}">
<local:ProviderView/>
</DataTemplate>
</Fluent:RibbonWindow.Resources>
<Fluent:RibbonWindow.DataContext><vm:MainWindowViewModel/></Fluent:RibbonWindow.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Fluent:Ribbon VerticalAlignment="Top"
IsDisplayOptionsButtonVisible="False"
Name="MainWindowRibbon"
SelectedTabChanged="MainWindowRibbon_SelectedTabChanged">
<!--Tabs-->
<Fluent:RibbonTabItem Header="Providers" Name="ProvidersTab">
<Fluent:RibbonGroupBox Header="Options" Width="120">
<Fluent:Button Header="Refresh"
Icon="{DynamicResource refresh}"
Command="{Binding LoadProvidersCommand}"/>
</Fluent:RibbonGroupBox>
</Fluent:RibbonTabItem>
</Fluent:Ribbon>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1">
<ContentControl Content="{Binding CurrentViewModel}"/>
</StackPanel>
</Grid>
</Fluent:RibbonWindow>
MainWindowViewModel.cs
namespace MyProgram.ViewModel
{
class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ViewModelBase _CurrentViewModel;
public ViewModelBase CurrentViewModel
{
get => _CurrentViewModel;
set
{
_CurrentViewModel = value;
OnPropertyChanged("CurrentViewModel");
}
}
public ProviderViewModel m_ProviderViewModel = new ProviderViewModel();
private ProviderManifestViewModel m_ProviderManifestViewModel = new ProviderManifestViewModel();
private ICommand _loadProvidersCommand;
public ICommand LoadProvidersCommand
{
get
{
return _loadProvidersCommand ?? (_loadProvidersCommand = new AsyncRelayCommand(Command_LoadProviders, GlobalUiCanExecute));
}
}
private AsyncRelayCommand<Guid> _loadProviderCommand;
public AsyncRelayCommand<Guid> LoadProviderCommand
{
get
{
return _loadProviderCommand ?? (_loadProviderCommand = new AsyncRelayCommand<Guid>(Command_LoadProvider));
}
}
#endregion
public MainWindowViewModel()
{
CurrentViewModel = m_ProviderViewModel;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void ShowProviderViewModel()
{
CurrentViewModel = m_ProviderViewModel;
}
private async Task Command_LoadProviders()
{
g_UiBusy = true;
CurrentViewModel = m_ProviderViewModel;
await m_ProviderViewModel.LoadProviders();
g_UiBusy = false;
}
private async Task<MyProvider?> Command_LoadProvider(Guid Id)
{
if (!GlobalUiCanExecute())
{
return null;
}
g_UiBusy = true;
var provider = await m_ProviderViewModel.LoadProvider(Id);
g_UiBusy = false;
return provider;
}
}
}
ProviderView.xaml
<UserControl x:Class="MyProgram.ProviderView"
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:local="clr-namespace:MyProgram.ViewModel"
xmlns:Fluent="urn:fluent-ribbon"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Name="ProviderViewControl">
<UserControl.DataContext><local:MainWindowViewModel/></UserControl.DataContext>
<Grid Name="ProvidersGrid">
<DataGrid Name="ProvidersDataGrid"
IsReadOnly="true"
AutoGenerateColumns="false"
ItemsSource="{Binding m_ProviderViewModel.Providers}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding Id}"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Source" Binding="{Binding Source}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
ProviderViewModel.cs
namespace MyProgram.ViewModel
{
class ProviderViewModel : ViewModelBase
{
private ObservableCollection<MyProvider> _providers;
public ObservableCollection<MyProvider> Providers
{
get => _providers;
set
{
_providers = value;
OnPropertyChanged("Providers");
}
}
public ProviderViewModel()
{
_providers = new ObservableCollection<MyProvider>();
}
public async Task LoadProviders()
{
var providers = await ProviderLoader.GetProviders();
if (providers == null)
{
return;
}
providers.ForEach(f => Providers.Add(f));
}
}
}
试试这个。 更换:
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1">
<ContentControl Content="{Binding CurrentViewModel}"/>
</StackPanel>
与:
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1">
<local:ProviderView DataContext="{Binding CurrentViewModel}"/>
</StackPanel>
删除:
<UserControl.DataContext><local:MainWindowViewModel/></UserControl.DataContext>