zoukankan      html  css  js  c++  java
  • 【WPF】MVVM动态修改Bingding的另一种思路——用Style样式

    问题场景:
    界面上有个ListBox控件,它的内容Item绑定了一个列表,即 ItemsSource =”{Binding StudentList}”。这个StudentList列表在该界面View对应的ViewModel中赋值。ListBox中每个元素Item都是一个Student实体类对象,核心代码如下:

    View:

    <ListBox 
        x:Name="studentLB" Margin="0" 
        VerticalAlignment="Top"
        HorizontalAlignment="Left"
        HorizontalContentAlignment="Stretch"
        ScrollViewer.CanContentScroll="False"
        ScrollViewer.HorizontalScrollBarVisibility="Disabled"
        ScrollViewer.VerticalScrollBarVisibility="Visible"
        ItemsSource="{Binding StudentList}">
    
         <!-- 流式布局,左对齐 -->
         <ListBox.ItemsPanel>
             <ItemsPanelTemplate>
                 <WrapPanel HorizontalAlignment="Left"/>
             </ItemsPanelTemplate>
         </ListBox.ItemsPanel>
    
        <!-- 条目的模板 -->
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Bingding FirstName}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    
    </ListBox>

    ViewModel:

    private ObservableCollection<Student> studentList;
    public ObservableCollection<Student> StudentList    // 前台ListBox的ItemsSource绑定该列表
    {
        get { return studentList; }
        set { SetProperty(ref studentList, value); }
    }

    Student.cs实体类

    public Class Student
    {
        public int Id { get; set; } // 唯一标识符
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    需求:现在条目中TextBlock 绑定的是Student对象的FirstName属性,如何将其改为绑定到LastName属性?

    思路:

    • 思路一:
      由于ItemsSource绑定到ViewModel中的StudentList列表,在Controller层从服务端获取到列表数据后,先进行加工,再赋值给StudentList。如先用一个临时列表TempList记录服务端返回的数据,然后遍历并修改该列表中的内容,想修改后的内容赋值给StudentList,如下:
    
    // 由于前台TextBlock绑定的是FirstName属性,现将FirstName的值改为LastName的值,即保持前台绑定不变的情况下,动态修改被绑定的属性的值。
    foreach(Student item in TempList)
    {
        //sourceList是从服务端获取的列表数据
        foreach(Student source in SourceList) 
        {
            if (item.id == source.id)
            {
                item.FirstName = source.LastName;
            }
        }
    }

    经过测试,虽然数据层的确发生了改变,但显示层却并没有更新。说好的MVVM呢?怎么会数据变了不自动更新界面的??

    • 思路二:
      将ListBox的界面改为采用Style样式,准备两种样式,区别仅在于TextBlock绑定到的是FirstName还是LastName。在Controller层动态修改ListBox使用的样式!如可以用一个按钮,每次点击都来回切换这两种样式。

    新建样式文件StudentStyle.xaml:

    <!-- ListBox样式 切换显示学生的FirstName/LastName -->
    <!-- 这两种样式仅有ListBoxItem中的TextBlock绑定到FirstName/LastName的不同 -->
    
    <ResourceDictionary  x:Class="YourProjectName.Presentation.Style.ListBox_StudentStyle"
            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">
    
        <!-- 样式1:显示FirstName -->
        <Style x:Key="firstNameStyle" TargetType="{x:Type ListBox}">
            <Setter Property="Margin" Value="0"/>
            <Setter Property="ItemsSource" Value="{Binding SpacePlansList}"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
            <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Visible"/>
    
            <!-- 流式布局 左对齐 -->
            <Setter Property="ListBox.ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <WrapPanel HorizontalAlignment="Left"/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
    
            <Setter Property="ListBox.ItemTemplate">
                  <Setter.Value>
                      <DataTemplate>
                          <TextBlock Text="{Binding FirstName}"/>
                      </DataTemplate>
                  </Setter.Value>
              </Setter>
          </Style>
    
          <!-- 样式2:显示LastName -->
          <Style x:Key="lastNameStyle" TargetType="{x:Type ListBox}">
              <Setter Property="Margin" Value="0"/>
              <Setter Property="ItemsSource" Value="{Binding SpacePlansList}"/>
              <Setter Property="BorderThickness" Value="0"/>
              <Setter Property="VerticalAlignment" Value="Top"/>
              <Setter Property="HorizontalAlignment" Value="Left"/>
              <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
              <Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
              <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
              <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Visible"/>
    
              <!-- 流式布局 左对齐 -->
              <Setter Property="ListBox.ItemsPanel">
                  <Setter.Value>
                      <ItemsPanelTemplate>
                          <WrapPanel HorizontalAlignment="Left"/>
                      </ItemsPanelTemplate>
                  </Setter.Value>
              </Setter>
    
              <Setter Property="ListBox.ItemTemplate">
                  <Setter.Value>
                      <DataTemplate>
                          <TextBlock Text="{Binding LastName}"/>
                      </DataTemplate>
                  </Setter.Value>
              </Setter>
          </Style>
    
    </ResourceDictionary>

    View:

    <!-- 引入样式资源 -->
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Presentation/Style/ListBox_StudentStyle.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    
    <!-- 使用样式,默认使用显示FirstName的样式 -->
    <ListBox x:Name="studentLB" Style="{StaticResource firstNameStyle}"/>

    控制层动态修改ListBox的样式:

    private Style listbox_FirstNameSytle;   // 显示FirstName的样式
    private Style listbox_LastNameSytle;    // 显示LastName的样式
    
    // 在控制层的初始化方法中,获取这两种样式
    public void Initialize()
    {
        var listBoxStyle = new ResourceDictionary
        {
            Source = new Uri("/YourProjectName;component/Presentation/Style/ListBox_StudentStyle.xaml", UriKind.RelativeOrAbsolute) // 指定样式文件的路径
        };
    
        listbox_FirstNameSytle = listBoxStyle["firstNameStyle"] as Style;
        listbox_LastNameSytle = listBoxStyle["lastNameStyle"] as Style;
    }
    
    private void btn_Click(object sender, RoutedEventArgs e)
    {
        // 根据按钮的状态,切换ListBox的样式
        if (view.btn.IsChecked == true)
        {
            view.studentLB.Sytle = listbox_LastNameSytle;
        }
        else
        {
            view.studentLB.Sytle = listbox_FirstNameSytle;
        }
    }

    经测试,该方法可行!


    小结:

    准备多套样式,通过动态修改样式来实现类似于动态修改绑定的效果。

  • 相关阅读:
    [代码]比较XML文件差异[cl_proxy_ui_utils=>show_xml_diff]
    查看日期属性(休息日、节假日、星期几)[DAY_ATTRIBUTES_GET]
    ABAP中字符串处理方法小结(一)
    ABAP中字符串处理方法小结(二)
    [问题解决]ALV可输入状态下输入金额/数量字段小数位数提前的问题
    自适应背景图
    js原型继承
    仿留言功能
    鼠标画图效果
    照片切换,24小块分散效果
  • 原文地址:https://www.cnblogs.com/guxin/p/wpf-mvvm-dynamically-modify-binding-by-style.html
Copyright © 2011-2022 走看看