【问题标题】:How do I have dynamic elements in a XAML WPF window如何在 XAML WPF 窗口中有动态元素
【发布时间】:2016-03-08 04:07:14
【问题描述】:

我有一个 MVVM WPF 应用程序,用于显示纸牌游戏的卡片。我只是在屏幕上显示它们,连续 4 张卡片,但有许多卡片在可观察的集合中。然而,这些卡片都是从一个基类继承而来的不同类型。作为用户控件,每种卡片类型都有其自己的截然不同的视图。所以,根据我的经验,我知道我可以将 ItemSource 属性绑定到我的 observable 集合,但是如何根据我的 ItemSource 中的项目类型指定使用某些用户控件?我推测我可以只加载每张卡的所有用户控件,并根据卡类型在转换器中打开或关闭每个控件的可见性,但这对我来说效率极低。有人知道吗?

【问题讨论】:

  • 您为每种卡类型声明一个DataTemplate。或者,如果卡片都是相同的类类型,但在属性中指定了它们的类型,那么您可以使用 DataTriggers

标签: c# wpf xaml mvvm user-controls


【解决方案1】:

您正在寻找DataTemplates。例如,假设您有以下类:

public class MyViewModel
{
  public List<PersonInfo> DataItems { get; }
    = new List<PersonInfo>()
      {
        new AgeInfo() { Age = 25 },
        new NameInfo() { Name = 13 }
      };
}

public abstract class PersonInfo
{
}

public class AgeInfo : PersonInfo
{
  public int Age { get; set; }
}

public class NameInfo : PersonInfo
{
  public int Name { get; set; }
}

你可以有一个 ItemsControl(或 ListBox、ListView 等),根据项目的数据类型显示每个项目:

<ItemsControl ItemsSource="{Binding DataItems}">
  <ItemsControl.Resources>

    <!-- Names are edited via textboxes -->
    <DataTemplate DataType="{x:Type local:NameInfo}">
      <StackPanel>
        <Label>Name:</Label>
        <TextBox Text="{Binding Name}" />
      </StackPanel>
    </DataTemplate>

    <!-- Ages are edited via slider -->
    <DataTemplate DataType="{x:Type local:AgeInfo}">
      <StackPanel>
        <Label>Age:</Label>
        <Slider Minimum="0" Maximum="150" Value="{Binding Age}" />
      </StackPanel>
    </DataTemplate>

  </ItemsControl.Resources>
</ItemsControl>

DataTemplate 的放置位置由您决定,但只要它位于控件的资源层次结构中,就会使用它。

【讨论】:

    【解决方案2】:

    创建一个ResourceDictionary 用于映射 UI(例如,将视图模型映射到模型)并注册它

    XAML

    <ResourceDictionary .....
                        xmlns:views="clr-namespace:...."
                        xmlns:viewModels="clr-namespace:...."
                        >
    
        <DataTemplate DataType="{x:Type viewModels:viewModels1}">
            <views:view1 />
        </DataTemplate>
    
        <DataTemplate DataType="{x:Type viewModels:viewModels2}">
            <views:view2 />
        </DataTemplate>
    
        ...
    
    </ResourceDictionary>
    

    C#

    public static class ViewsMapping
    {
        private static bool _isUIMappingRegistered = false;
    
        public static void Register()
        {
            if (!_isUIMappingRegistered)
            {
                ResourceDictionary MyResourceDictionary = new ResourceDictionary();
                MyResourceDictionary.Source = new Uri(".....", UriKind.Relative);
                Application.Current.Resources.MergedDictionaries.Add(MyResourceDictionary);
                _isUIMappingRegistered = true;
            }
        }
    }
    

    然后将observable collection更改为ObservableCollection&lt;BaseClass&gt;,选中的视图将呈现派生类视图。

    【讨论】:

    • 对于 SO 的要求来说,这似乎有点过火了。他们不需要在自己的 ResourceDictionary 中,即使他们这样做了,您也不需要将其合并到代码中,您只需将其添加到应用程序 xaml 中即可。
    • 嗨,马克,你当然是对的,解决方案只需要“将 ViewModel 与视图匹配”,但我仍然认为我回答的是最优雅、有序和可维护的解决方案。
    • 感谢您对此答案的输入!虽然我确实喜欢这个解决方案的简单性,但我无法让这个项目创建/处理资源字典。这有点奇怪,但团队中的每个成员都有特定的角色,他们可以做特定的事情,而创建一个全局资源字典对于这个项目来说是我无法做到的。
    猜你喜欢
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    • 2020-10-26
    • 1970-01-01
    • 2011-01-07
    • 2016-07-23
    • 2010-10-04
    • 2021-08-06
    相关资源
    最近更新 更多