【问题标题】:C# Generic User Control WPFC# 通用用户控件 WPF
【发布时间】:2011-07-15 16:43:58
【问题描述】:

我正在尝试构建一个显示字典内容的用户控件

问题是我不知道用户控件中键和值的类型,我在创建用户控件时就知道,但 C# 似乎不想让我创建通用用户控件这样我就可以将它们传递给托管字典

public class MyCtrl<TKey,TValue> : UserControl
{
    private Dictionary<TKey,TValue>
}

因为它引用了 .\Obj\Debug\MyCtrl.g.i.cs 中生成的文件 这是只读的

向我展示的唯一解决方案是从头开始创建类,不让设计人员处理任何格式,是否有更好的可能性?

为了在我的代码中的大约 8-10 个位置提供一些背景知识,我需要用户填充值的字典,而不是构建 8-10 个控件,它们都做同样的事情但具有不同的类型(实际上很多当时唯一的区别是哪个枚举作为键)我想要一个控件来处理这个

【问题讨论】:

    标签: c# wpf generics user-controls


    【解决方案1】:

    您可以像不使用 XAML 一样使用泛型。但是如果你想使用 XAML 来定义你的控件,你不能使用泛型

    【讨论】:

    • 我计划将其添加到 XAML 的方式是通过作为容器的内容控件,然后我在代码中实例化泛型并将其添加到容器中。
    【解决方案2】:

    我终于有了一个可行的答案。

    我围绕使用对象的 Hashtable 构建控件

    然后为对象类添加扩展

        public static bool TryParse<TType>(this object obj, out TType result) 
        {
            try
            {
                result = (TType)Convert.ChangeType(obj, typeof(TType));
                return true;
            }
            catch
            {
                result = default(TType);
                return false;
            }
        }
        public static TType Parse<TType>(this object obj) where TType : struct
        {
            try
            {
                return (TType)Convert.ChangeType(obj, typeof(TType));
            }
            catch
            {
                throw new InvalidCastException("Cant cast object to " + typeof(TType).Name);
            }
        }
    

    然后添加一个通用属性,该属性调用扩展以将对象转换为字典

    【讨论】:

      【解决方案3】:

      正如 Steyca 所提到的,您不能在 XAML 中使用泛型。但是,您可以创建您的通用用户控件,并从该通用类派生特定类型,您可以在 XAML 中使用。这至少在一定程度上减少了代码重复。

      作为旁注,如果您自己支持 XAML 中的泛型,则可以通过将类型作为属性值传递来实现。至少,我相信这是this article 的意图,但我还没有时间亲自尝试。

      【讨论】:

      • 我将注意力转移到 Control 类上,而不是在没有设计器支持的情况下实例化的 UserControl 转储 XAML。这意味着我必须计划所有图形而无法看到它们,但至少摆脱了我无法覆盖的生成代码
      【解决方案4】:

      如果您希望能够在 xaml 中使用您的控件,您将无法以这种方式创建通用 UserControl,因为 WPF 不支持它。您将如何在 xaml 中以声明方式实例化该类型?

      我会看看其他控件如何处理这样的事情。例如,ListBox 允许您填充项目列表,而无需考虑解析到其 ItemsSource 中的集合类型。

      <ListBox ItemsSource="{Binding DictionaryItems}" >
         <ListBox.ItemTemplate>
            <DataTemplate>
               <StackPanel Orientation="Horizontal">
                  <TextBlock Text="{Binding Key}" />
                  <TextBlock Text="{Binding Value}" />
               </StackPanel>
            </DataTemplate>
         </ListBox.ItemTemplate>
       </ListBox>
      

      对于您的各种字典类型,您可以为每种不同的字典类型设置多个 DataTemplate,并使用 TemplateSelector 进行切换。

      public class SomeSelector : DataTemplateSelector
      {
         public DataTemplate Template1 { get; set; }
         public DataTemplate Template2 { get; set; }
      
         public override DataTemplate SelectTemplate(object item, DependencyObject container)
         {
            if (item is IDictionary<string, int>) 
            {
               return Template1;
            }
      
            return Template2;
         }
      }
      

      然后在 xaml 中

      <UserControl.Resources>
         <DataTemplate x:Key="Template1">
            <StackPanel Orientation="Horizontal">
               <TextBlock Text="{Binding Key}" />
               <TextBlock Text="{Binding Value}" />
            </StackPanel>
         </DataTemplate>
      
         <DataTemplate x:Key="Template2>
            <StackPanel Orientation="Vertical">
               <TextBlock Text="{Binding Key}" />
               <TextBlock Text="{Binding Value}" />
            </StackPanel>
         </DataTemplate>
      
         <SomeSelector x:Key="SomeSelector" Template1="{StaticResource Template1}" Template2="{StaticResource Template2}" />
      </UserControl.Resources>
      
      <ListBox ItemsSource="{Binding DictionaryItems}" ItemTemplateSelector="{StaticResource SomeSelector}" />
      

      【讨论】:

      • 我不得不说,当 MS 重写 .net 以包含很好的类型安全泛型时,他们并没有决定将其包含在他们的控件设计器中,这让我感到很奇怪。毕竟我确信我不是唯一一个可以看到拥有类型安全控件的好处的人
      【解决方案5】:

      您最好使用 MVVM,并将所有泛型类型和约束放在您的视图模型上,而不是您的视图上。

      【讨论】:

        【解决方案6】:

        之所以要设置一个通用的用户控件是因为你不知道其中的key和value是什么:

         private Dictionary<TKey,TValue> someDictionary;
        

        我相信类型会提前出现。如果是这种情况,那么将您的字典声明为:

        private Dictionary<dynamic,dynamic> someDictionary
        

        那么您将能够向其添加任何类型的键和值。因此,请确保如果您的键是一个 int 并且您的值是一个字符串,那么请始终遵循该模式。例如,编译器会让你编译这段代码:

                Dictionary<dynamic, dynamic> myDictionAry = new Dictionary<dynamic, dynamic>();
                myDictionAry.Add(1, "kd");
                myDictionAry.Add("kjdf", 3);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-26
          相关资源
          最近更新 更多