我对WPF/MVVM和构建运行“作业”的应用程序是新手。应用程序目前看起来像这样:
MainView
包含一些按钮和一个ContentControl
。ContentControl
显示一些子视图,如JobsView
或ApplicationSettingsView
,具体取决于所选的菜单项。JobsView
包含代表作业的块。它显示了一些作业信息、作业功能和编辑/配置作业的编辑按钮(Bewerk)。
当我点击“Bewerk”按钮时,MainView
的ContentControl
中应显示作业特定的编辑视图。我正在寻找最好的方式来处理这个导航。重要的是,我在JobEditViewModel
中有相关作业的示例。
我试过:
1.在MainViewModel
中创建函数时,将函数传递给JobsViewModel
(https://linuxhint.com/passing-function-as-parameter-c-sharp/)。这不起作用,因为JobsViewModel
被调用了两次(在调试过程中注意到)。一次来自MainViewModel
,一次来自?第二次,它调用了没有参数的构造函数,所以我不能调用传递的函数。经过一些研究,我也读到了将参数传递给ViewModel
构造函数的不好的做法。
...
namespace ECRO.MVVM.ViewModel
{
internal class MainViewModel : ObservableObject
{
private object _currentView;
private Engine engine;
public JobsViewModel JobsVm { get; set; }
public SettingsViewModel SettingsVm { get; set; }
public RelayCommand JobViewCommand { get; set; }
public RelayCommand SettingsViewCommand { get; set; }
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
Task.Run(() =>
{
engine = Engine.Instance;
});
JobsVm = new JobsViewModel((viewModel) => { this._currentView = viewModel; });
SettingsVm = new SettingsViewModel();
_currentView = JobsVm;
// Initialize relay commands
JobViewCommand = new RelayCommand(o => { CurrentView = JobsVm; });
SettingsViewCommand = new RelayCommand(o => { CurrentView = SettingsVm; });
}
}
}
...
namespace ECRO.MVVM.ViewModel
{
class JobsViewModel
{
private ObservableCollection<RJob> _jobs;
public ObservableCollection<RJob> Jobs { get => _jobs; set { _jobs = value; } }
public RelayCommand ForceRunJobOnceCmd { get; set; }
public RelayCommand StartJobCmd { get; set; }
public RelayCommand StopJobCmd { get; set; }
public RelayCommand EnableJobCmd { get; set; }
public RelayCommand DisableJobCmd { get; set; }
public RelayCommand EditJobCmd { get; set; }
public string btnEnableDisableContent { get => "test"; }
public JobsViewModel() {
}
public JobsViewModel(Action<BaseViewModel> changeContentView)
{
_jobs = Engine.Instance.RJobs;
ForceRunJobOnceCmd = new RelayCommand(o => { ((RJob)o).ForceRunOnce(); });
StartJobCmd = new RelayCommand(o => { ((RJob)o).Start(); });
StopJobCmd = new RelayCommand(o => { ((RJob)o).Stop(); });
EnableJobCmd = new RelayCommand(o => { ((RJob)o).Enable(); });
DisableJobCmd = new RelayCommand(o => { ((RJob)o).Disable(); });
EditJobCmd = new RelayCommand(o => { changeContentView(new VerzamelqueryViewModel()); });
}
}
}
1.使用事件和委托。(https://www.tutorialsteacher.com/csharp/csharp-event)这也不起作用。由于某种原因,MainView
中的事件处理程序未被调用/触发。使用这种解决方案也感觉不太好,因为我不知道如何注册一个事件处理程序,当子视图不是从MainView
创建/启动的,而是从另一个子视图创建/启动的。(例如,EditJobView
将得到一个子子视图,以显示有关Job
的日志,并包括一个返回按钮,以返回到EditJobView
)它也感觉不是真正的通用/可重用。(需要为处理导航的每个子视图实现新的事件和事件处理程序)
...
namespace ECRO.MVVM.ViewModel
{
internal class MainViewModel : ObservableObject
{
private object _currentView;
private Engine engine;
public JobsViewModel JobsVm { get; set; }
public SettingsViewModel SettingsVm { get; set; }
public RelayCommand JobViewCommand { get; set; }
public RelayCommand SettingsViewCommand { get; set; }
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
Task.Run(() =>
{
engine = Engine.Instance;
});
//JobsVm = new JobsViewModel((viewModel) => { });
JobsVm = new JobsViewModel();
JobsVm.ChangeView += (object sender, BaseViewModel newView) => { _currentView = newView; };
SettingsVm = new SettingsViewModel();
_currentView = JobsVm;
// Initialize relay commands
JobViewCommand = new RelayCommand(o => { CurrentView = JobsVm; });
SettingsViewCommand = new RelayCommand(o => { CurrentView = SettingsVm; });
}
}
}
...
namespace ECRO.MVVM.ViewModel
{
class JobsViewModel
{
private ObservableCollection<RJob> _jobs;
public ObservableCollection<RJob> Jobs { get => _jobs; set { _jobs = value; } }
public RelayCommand ForceRunJobOnceCmd { get; set; }
public RelayCommand StartJobCmd { get; set; }
public RelayCommand StopJobCmd { get; set; }
public RelayCommand EnableJobCmd { get; set; }
public RelayCommand DisableJobCmd { get; set; }
public RelayCommand EditJobCmd { get; set; }
public string btnEnableDisableContent { get => "test"; }
public event EventHandler<BaseViewModel> ChangeView;
public JobsViewModel()
{
_jobs = Engine.Instance.RJobs;
ForceRunJobOnceCmd = new RelayCommand(o => { ((RJob)o).ForceRunOnce(); });
StartJobCmd = new RelayCommand(o => { ((RJob)o).Start(); });
StopJobCmd = new RelayCommand(o => { ((RJob)o).Stop(); });
EnableJobCmd = new RelayCommand(o => { ((RJob)o).Enable(); });
DisableJobCmd = new RelayCommand(o => { ((RJob)o).Disable(); });
EditJobCmd = new RelayCommand(o => { ChangeView?.Invoke(this, new VerzamelqueryViewModel()); });
}
}
}
处理我的案例的最佳实践/WPF-MVVM方法是什么?
- 更新日期:在需要的情况下,它们...
主视图:
<Window x:Class="ECRO.MVVM.View.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:local="clr-namespace:ECRO.MVVM.View"
xmlns:viewModel="clr-namespace:ECRO.MVVM.ViewModel"
mc:Ignorable="d"
Height="880" Width="1000"
WindowStyle="None"
ResizeMode="NoResize"
Background="Transparent"
AllowsTransparency="True"
Title="MainWindow">
<Window.DataContext>
<viewModel:MainViewModel/>
</Window.DataContext>
<Border
Background="#272537"
CornerRadius="20">
<WrapPanel>
<Border Height="35"
Width="1000"
Margin="0"
CornerRadius="20 20 0 0"
Background="#22202f">
<DockPanel>
<DockPanel Width="930"
Background="Transparent"
PreviewMouseDown="drawWindow">
<TextBlock Text="ECRO - ECB Robot" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="15" FontWeight="Bold"/>
</DockPanel>
<DockPanel HorizontalAlignment="Right"
Margin="0 5 10 0">
<Button Content="—"
Width="20" Height="20"
Margin="5 5 5 5"
Background="Transparent"
BorderThickness="0"
Foreground="White"
FontSize="15"
Click="minimizeApplication"/>
<Button Content="X"
Width="20" Height="20"
Margin="5 5 5 5"
Background="Transparent"
BorderThickness="0"
Foreground="White"
FontSize="15"/>
</DockPanel>
</DockPanel>
</Border>
<Grid Height="845" Width="1000">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="ECB Robot"
Grid.Row="0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Foreground="White"
FontSize="22"
Margin="20 0 0 0"/>
<StackPanel Grid.Row="1"
Margin="0 10 0 0">
<RadioButton Content="Jobs"
Style="{StaticResource MenuButton}"
IsChecked="True"
Command="{Binding JobViewCommand}"
/>
<RadioButton Content="Settings"
Style="{StaticResource MenuButton}"
Command="{Binding SettingsViewCommand}"/>
<RadioButton Content="Stop robot"
Style="{StaticResource MenuButton}"/>
</StackPanel>
<TextBox Width="250"
Height="40"
VerticalContentAlignment="Center"
HorizontalAlignment="Left"
Margin="5"
Grid.Row="0"
Grid.Column="1"
Style="{StaticResource Textbox}"/>
<ContentControl Grid.Row="1"
Grid.Column="1"
Margin="10"
Content="{Binding CurrentView}"/>
</Grid>
</WrapPanel>
</Border>
</Window>
职位浏览:
<UserControl x:Class="ECRO.MVVM.View.JobsView"
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"
xmlns:viewModel="clr-namespace:ECRO.MVVM.ViewModel"
xmlns:widget="clr-namespace:ECRO.MVVM.Widgets"
mc:Ignorable="d" d:DesignWidth="800" Height="780">
<UserControl.DataContext>
<viewModel:JobsViewModel/>
</UserControl.DataContext>
<StackPanel>
<TextBlock Text="Jobs" Style="{StaticResource HeaderTextBlock}"/>
<ScrollViewer Width="780" Height="700">
<ItemsControl ItemsSource="{Binding Path=Jobs}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModel:JobWidgetViewModel}">
<widget:JobWidget/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</UserControl>
JobWidget:
<UserControl x:Class="ECRO.MVVM.Widgets.JobWidget"
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="110" d:DesignWidth="760">
<!--<UserControl.DataContext>
<viewModel:JobWidgetViewModel/>
</UserControl.DataContext>-->
<Grid>
<Grid Height="110" Width="760">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="677*"/>
<ColumnDefinition Width="123*"/>
</Grid.ColumnDefinitions>
<Border CornerRadius="10"
Background="#353340"
Height="100"
Margin="10,0,10,0"
VerticalAlignment="Center" Grid.ColumnSpan="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<!--<RowDefinition Height="50*"/>-->
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical"
Margin="5 2 0 0">
<Label Content="Naam" FontWeight="Bold" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
<Label Content="Status" FontWeight="Bold" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
<Label Content="Laatste run" FontWeight="Bold" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
<Label Content="Volgende run" FontWeight="Bold" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
<Label Content="Schema" FontWeight="Bold" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
</StackPanel>
<StackPanel Grid.Column="1" Margin="2">
<Label Content="{Binding Name}" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
<Label Content="{Binding Status}" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
<Label Content="{Binding LastRun}" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
<Label Content="{Binding NextRun}" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
<Label Content="{Binding Schedule.Type }" Style="{StaticResource DefaultLabel}" Margin="0 -5 0 -5"/>
</StackPanel>
<Border Grid.RowSpan="2"
Grid.Column="2"
Grid.Row="0"
HorizontalAlignment="Right"
Margin="0 0 20 0">
<StackPanel Orientation="Horizontal">
<Button Content="Forceer run"
Style="{StaticResource DarkButton}"
Width="100"
Command="{Binding DataContext.ForceRunJobOnceCmd, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
<Button Content="Start"
Style="{StaticResource DarkButton}"
Command="{Binding DataContext.StartJobCmd, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
<Button Content="Stop"
Style="{StaticResource DarkButton}"
Command="{Binding DataContext.StopJobCmd, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
<Button x:Name="btnEnable"
Content="Enable"
Style="{StaticResource DarkButton}"
Command="{Binding DataContext.EnableJobCmd, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
<Button x:Name="btnDisable"
Content="Disable"
Style="{StaticResource DarkButton}"
Command="{Binding DataContext.DisableJobCmd, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
<Button Content="Bewerk"
Style="{StaticResource DarkButton}"
Command="{Binding Main.EditJobCmd, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
</StackPanel>
</Border>
</Grid>
</Border>
</Grid>
</Grid>
</UserControl>
2条答案
按热度按时间xesrikrc1#
您可以使用事件聚合器或信使来引发事件或将消息从一个视图模型传递到另一个视图模型。这个概念在this blog post中解释。
其思想是,
JobsViewModel
引发一个MainViewModel
处理的“导航”事件,而不引入视图模型之间的直接依赖关系。另一种选择是使用
INavigate
或INavigationService
接口注入JobsViewModel
,该接口用于切换视图,例如执行MainViewModel
当前执行的操作。第三种选择是使用
{RelativeSource}
从JobsView
直接绑定到MainViewModel
的命令:dauxcl2d2#
当我点击“Bewerk”按钮时,一个特定于作业的编辑视图应该显示在主视图的ContentControl中。我正在寻找最好的方式来处理这个导航。
这个需求意味着MainViewModel负责导航,而不是JobsViewModel。所以EditJobCmd命令应该在MainViewModel中,而不是在JobsViewModel中(并且它消除了通过构造函数传递委托的必要性)