硬代码或抽象视图



我正在使用MVVM开发一个WPF应用程序,通过灯箱风格的弹出窗口显示错误消息/特殊对话框。这些子视图是由ContentControl在主视图中显示的用户控件。

到目前为止,每个子视图都被硬编码以执行单个功能(例如,显示错误或要求用户首先备份)。但随着应用程序的发展,我从大多数控件中看到了相同的设计模式:

  • 左上角的图标图像
  • 图标旁边的标题文本
  • 中间的消息文本
  • 右下角有2个按钮

有了MVVM,我应该能够抽象这个模式,并重用这个控件来显示错误,要求用户备份任何东西,只需绑定即可。我甚至应该能够绑定按钮的名称,如果不使用的话,甚至可以隐藏1。。。诸如此类的东西。

但是我应该吗?这样做对性能有好处吗?当有8个子视图都具有相同的网格模式时,这似乎属于DRY。

干燥与性能无关。

这是为了节省编写代码和维护阶段的时间。

虽然制作一个通用的可重复使用的窗口会更优雅,但这可能需要一定的成本。

这项工作的花费比你得到的好处还多吗?是否合理化为一个可能更复杂的观点的决定应该基于一种成本效益分析。

您应该考虑的因素:

每个视图需要多长时间?

每个中的功能有多复杂?

制作一个泛型需要付出多少努力?

有多少例外情况,使其通用化会有多复杂?

让这个通用的功能变得晦涩难懂吗?在多大程度上会让维护成本更高?

你有多大可能不得不改变这些东西的外观?

如果你不太可能改变外观,有一些边缘情况会使通用视图变得复杂,并且注入你的功能会变得复杂,那么只需将标记复制并粘贴到每个视图中就有意义了。

编辑:

请记住,样式是可重复使用的。

这里有一个具体的标记需要考虑。

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Path Data="{Binding IconGeometry}"
Stretch="Fill"
Fill="Black"
Height="28"
Width="28"/>
<TextBlock Text="{Binding Heading}"/>
</StackPanel>
<TextBlock Grid.Row="1"
Text="{Binding Message}"/>
<ItemsControl 
Grid.Row="2"
ItemsSource="{Binding NamedCommandCollection}"
HorizontalAlignment="Right">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding ButtonText}" Command="{Binding ButtonCommand}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>

您为此提供了一个视图模型。

此视图模型实现inotifypropertychanged,并为标题、消息等提供字符串属性。

这里不太明显的是一条带有几何图形而不是图像的路径。这取决于你的肖像画会是什么样子,但简单的单色形状现在很常见。

您可以在资源字典中定义几何图形,从中获取适当的几何图形并将其作为属性提供。合并后的资源字典在application.current.resources中,它几乎是一个内存中的对象字典,由x:key字符串键控。

按钮由itemscontrol生成,该控件将其项目模板化为水平的按钮行。构建表示按钮的视图模型。

name的字符串属性和ButtonCommand的relaycommand或delegatecommand。

让我们称之为ButtonVM。

将一个ButtonVM添加到observablecollection属性NamedCommandCollection中,就会出现一个按钮。加一、二、三。不管你喜欢多少。你可以让ButtonVM只接受你构建和提供的relaycommand,或者自己有一个relaycommant,然后注入一个操作。您可以在动态构建操作时捕获变量。

命令也有canexecute。您可以使用它来优化何时可以单击按钮。EG我在一个基本视图模型中有一个属性IsBusy,我用它来标记是否有任何命令正在"运行",以避免快速双击破坏一切。

这是:

public class BaseViewModel : INotifyPropertyChanged
{
private bool isBusy;
[IgnoreDataMember]
public bool IsBusy
{
get => isBusy;
set => ToVal(ref isBusy, value, nameof(IsBusy));
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void ToObj<T>(ref T backer, T value, [CallerMemberName] string propertyName = null)
{
backer = value;
this.RaisePropertyChanged(propertyName);
}
public void ToVal<T>(ref T backer, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(backer, value))
{
return;
}
backer = value;
this.RaisePropertyChanged(propertyName);
return;
}
}

Icommand有一个canexecute bool,如果为false,它将禁用命令绑定到的控件。但是,这取决于commandmanager决定重新查询canexecute并禁用该控件。在某些情况下,这种情况不会发生得足够快。因此,最好使用bool来保护命令中的代码。

一些真实代码的一个相当随机的例子:

private RelayCommand newMapCommand;
public RelayCommand NewMapCommand
{
get
{
return newMapCommand
?? (newMapCommand = new RelayCommand(
() =>
{
if (IsBusy)
{
return;
}
ResetMap();
IsBusy = false;
},
( ) => !IsBusy
));
}
}

Relaycommand在mvvmlight中。由于我现在在net core工作,并且commandwpf对net old有依赖性,所以我获取了我想要的mvvmlight的位的源代码。我保留了名称空间,因为Laurent可能最终会解决这个问题,或者net 5可能会避免这个问题。

用户控件本身可以包含一个用户控件。如果你想要灵活性,那么它可以有一个内容控制和模板来确定与它的内容绑定的内容。

这首先用于视图模型,这是切换导航内容等的常见方式。我写了一个例子来解释页面的害处:^)https://social.technet.microsoft.com/wiki/contents/articles/52485.wpf-tips-and-tricks-using-contentcontrol-instead-of-frame-and-page-for-navigation.aspx

最新更新