ViewModel的公开数据取决于View的大小

时间:2014-08-19 19:29:35

标签: c# wpf xaml mvvm

背景

我是初学者WPF开发人员。作为一个学习练习,我试图使用带有MVVM模式的WPF重新创建Visual Studio的内存窗口。对于那些不熟悉这个窗口的人。

enter image description here

实施大纲

为简单起见,我们假设内存是只读的,不会改变。根据MVVM模式,我们定义以下实体:

模型

模型是一个包含2个属性的简单对象:

  1. long Address
    • 数据驻留在内存中的地址
  2. byte[] Data
  3. 模型表示定义的内存地址(Data)的字节序列(Address)。

    视图模型

    ViewModel 模型作为输入并公开以下属性:

    1. string Addresses

      • 表示同一行上显示的字节地址的string。引用背景部分中的图片,Addresses string包含第一列的内容,即:

        string Addresses = "0x022699B0" + '\n' + "0x022699C8" + '\n' + "0x022699E0" + '\n' + ...

    2. string HexBytes

      • 模型Data的十六进制表示形式。引用背景部分中的图片,HexBytes string包含第二列的内容,即:

        string HexBytes = "c0 ac 45 68 06 ..."

    3. string ASCIIBytes

      • 模型Data的ASCII表示形式。引用背景部分中的图片,ASCIIBytes string包含第三列的内容,即:

        string ASCIIBytes = "A.Ch....9.1.1.1.2.5 ..."

    4. 总而言之, ViewModel 视图友好格式显示给出模型的数据。

      视图

      查看可能最容易通过XAML代码段进行描述:

      <UserControl x:Class="HexEditor.HexView"
               xmlns  ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:z="clr-namespace:HexEditor">
      
      <UserControl.DataContext>
          <z:MemoryViewModel/>
      </UserControl.DataContext>
      
      <Grid>
          <Grid.RowDefinitions>
              <RowDefinition/>
          </Grid.RowDefinitions>
      
          <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto"/>
              <ColumnDefinition Width="3*"/>
              <ColumnDefinition Width="1*"/>
          </Grid.ColumnDefinitions>
      
          <TextBox Grid.Column="0" IsReadOnly="True" Cursor="Arrow" FontFamily="Consolas" Text="{Binding Addresses, Mode=OneWay}"/>
          <TextBox Grid.Column="1" IsReadOnly="True" TextWrapping="Wrap" Cursor="Arrow" FontFamily="Consolas" Text="{Binding HexBytes, Mode=OneWay}" />
          <TextBox Grid.Column="2" IsReadOnly="True" TextWrapping="Wrap" Cursor="Arrow" FontFamily="Consolas" Text="{Binding ASCIIBytes, Mode=OneWay}" />
      </Grid>
      

      请注意,已在第二列和第三列上设置了TextWrapping属性。这允许我们每行显示可变数量的字节,具体取决于Window的大小。

      问题

      我遇到的问题是 ViewModel 必须公开的属性取决于视图的大小。在MVVM模式中, ViewModel 应独立于视图。这不是HexBytesASCIIBytes的问题,因为它们是从模型生成的。问题是如何生成Addresses?要生成此string,我必须知道将在每一行上显示的字符数(以便我可以计算下一行的第一个字节的正确地址)。不幸的是,这只是视图所知道的,因为它取决于视图的大小。

      提议的解决方案

      我尝试解决此问题的方法是捕获查看代码隐藏中的SizeChanged事件,并使用TextBox.GetLineLength方法通知 ViewModel 一行可以容纳多少字节。然后, ViewModel 可以使用此值来生成正确的Addresses属性。

      讨论

      关于我提出的解决方案,我有两个问题:

      1. 是否有更优雅的方式通知 ViewModel 可以在第二行TextBox的第一行显示的字符数?
      2. 我是否正在使用正确的控件和正确的方法来获得最终结果(即重新创建Visual Studio的内存窗口)?
      3. 非常感谢任何意见或建议。

2 个答案:

答案 0 :(得分:1)

当您在事件处理程序中捕获该值时,只需更新从该事件处理程序中保留此值的VM属性,这样一旦事件触发,VM将始终具有该值...

由于您的VM已经是视图的DataContext,因此这不会破坏MVVM模式,并且更新VM属性的值非常简单:

这一切都在您的View代码隐藏中:

字段:

private MyViewModel vm;

在视图构造函数中(如果动态分配DataContext,则为DataContextChanged事件):

vm = DataContext as MyViewModel; //Get VM instance from view's DataContext

在事件处理程序中:

vm.AddressLenght = TextBox.GetLineLength;

答案 1 :(得分:0)

  

是否有更优雅的方式通知ViewModel可以在1行显示的字符数

我认为你的方法是最好的。您想到的结果 - 带有推断列标题的可变列宽 - 意味着视图模型对视图的依赖性。 (有些MVVM方法使用某种&#34;消息传递和#34;框架(例如,事件聚合器)在视图和视图模型之间进行通信,但这只是让视图直接调用方法的更正式的方式。视图模型。)此外,您需要在任何情况下处理SizeChanged事件,以便在每个重新调整大小时重新生成列标题。

  

我是否正在使用正确的控件和更正确的方法来获得最终结果

看起来很好。您可以考虑使用ObservableCollection<string>以及ItemsControlListBox(而不是带有换行符的扁平字符串)。这样您就可以更好地控制用户界面,从而实现突出显示/选择单个地址等内容。同步滚动地址/字节;和虚拟化面板,以处理更大的数据集,而不会降低用户界面。