【问题标题】:WPF MVVM Custom Control issueWPF MVVM 自定义控件问题
【发布时间】:2011-03-09 22:46:46
【问题描述】:

我正在尝试创建自定义标签云控件。我希望它工作的方式是用户可以在 itemsSource 中给它一个字符串集合,转换后的字符串将显示在 UI 中。

目前我拥有的是一个标签云 MVVM 应用程序,它采用集合字符串(包含重复项)并使用 collectionView 按“名称”对它们进行分组,然后使用转换器来确定每个字符串(标签)的 FontSize基于原始集合中该 string.name 的计数。

所以我的问题是如何将其更改为我想要的自定义控件?我知道我将不得不在我的代码隐藏中扩展列表框以公开 itemsSource 但我感到困惑的部分是如何处理分组逻辑(collectionView),因为我目前正在绑定到一个分组集合(没有重复)而我希望用户能够提供任何集合。请让我知道这是否清楚。?

分组逻辑

public class TagCloudControl : ListBox
{
    public ObservableCollection<TagModel> Tags { get; set; }

    static TagCloudControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TagCloudControl), new FrameworkPropertyMetadata
            (typeof(TagCloudControl)));
    }

    public static DependencyProperty tagsProperty = DependencyProperty.Register("tags",
        typeof(IEnumerable),
        typeof(TagCloudControl));

    public CollectionView GroupedTagsView { get; set; }

    public TagCloudControl()
    {

        ItemsSource = Tags;

        //group my labels by "name" property
        GroupedTagsView = (ListCollectionView)CollectionViewSource.GetDefaultView(Tags);
        GroupedTagsView.GroupDescriptions.Add(new PropertyGroupDescription("Name")); 

    }

    public IEnumerable tags
    {
        get { return (IEnumerable)GetValue(tagsProperty); }
        set { SetValue(tagsProperty, value); }
    }
}

【问题讨论】:

    标签: c# wpf


    【解决方案1】:
    1. 你没有理由扩展 ListBox 而不是 ItemsControl

    2. 为什么要走自定义控件路径而不是用户控件路径?

    我要做的是定义一个具有 IEnumerable 标签的依赖属性的 UserControl。 然后我会将此 DP 绑定到 ViewModel(虽然实际上不需要使用 MVVM inside 控件,但这真的取决于你......)然后 ViewModel 可以通过这些字符串,并公开另一个属性 - ObservableCollection 标签, Tag 应该是这样的:

    class Tag
    {
        public String Name {get;set}
        public double Size {get;set;}
    
    }
    

    XAML 将只包含一个 ItemsControl,它将数据绑定到 ViewModel 中的标记列表。这样它就可以完全按照您的意愿工作。

    <UserControl>
        <ItemsControl ItemsSource="{Binding Tags}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" FontSize="{Binding Size}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </UserControl>
    

    【讨论】:

    • 感谢您的回复。我正在努力解决的部分是定义依赖属性并将其绑定到上述 ViewModel。你能帮忙吗?
    【解决方案2】:

    下面是一些示例代码,我相信它可以满足您的需求。最大字体大小被硬编码为 32,但实际上应该是另一个依赖属性。

    编辑:该控件现在具有标签和单词集合的 DP。

    XAML:

    <UserControl x:Class="TagCloudDemo.TagCloudControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:TagCloudDemo="clr-namespace:TagCloudDemo">
    
    <UserControl.Resources>
        <TagCloudDemo:WeightToSizeConverter x:Key="WeightToSizeConverter" />
    </UserControl.Resources>
    
    <ScrollViewer HorizontalScrollBarVisibility="Auto">
        <ItemsControl 
            ItemsSource="{Binding Path=Tags, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TagCloudDemo:TagCloudControl}}}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=Name}" FontSize="{Binding Path=Weight, Converter={StaticResource WeightToSizeConverter}}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
    

    后面的代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    namespace TagCloudDemo
    {
        public partial class TagCloudControl : UserControl
        {
            public TagCloudControl()
            {
                InitializeComponent();
            }
    
            public IEnumerable<string> Words
            {
                get { return (IEnumerable<string>)GetValue(WordsProperty); }
                set { SetValue(WordsProperty, value); }
            }
    
            public static readonly DependencyProperty WordsProperty =
                DependencyProperty.Register("Words", 
                                            typeof(IEnumerable<string>), 
                                            typeof(TagCloudControl), 
                                            new UIPropertyMetadata(new List<string>(), WordsChanged));
    
            public IEnumerable<Tag> Tags
            {
                get { return (IEnumerable<Tag>)GetValue(TagsProperty); }
                set { SetValue(TagsProperty, value); }
            }
    
            public static readonly DependencyProperty TagsProperty =
                DependencyProperty.Register("Tags", 
                                            typeof(IEnumerable<Tag>), 
                                            typeof(TagCloudControl),
                                            new UIPropertyMetadata(new List<Tag>(), TagsChanged));
    
            private static void WordsChanged(object sender, DependencyPropertyChangedEventArgs e)
            {
                TagCloudControl tagCloudControl = sender as TagCloudControl;
                tagCloudControl.Tags = TagCloudDemo.Tag.CreateTags(tagCloudControl.Words);
            }
    
            private static void TagsChanged(object sender, DependencyPropertyChangedEventArgs e)
            {
                TagCloudControl tagCloudControl = sender as TagCloudControl;
                WeightToSizeConverter converter = tagCloudControl.FindResource("WeightToSizeConverter") as WeightToSizeConverter;
                if (converter != null && tagCloudControl.Tags != null)
                {
                    converter.MaxWeight = tagCloudControl.Tags.Max(t => t.Weight);
                }
            }
        }
    
        public class WeightToSizeConverter : IValueConverter
        {
            public int MaxWeight { get; set; }
    
            #region IValueConverter Members
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                int weight = (int)value;
                return 32 * MaxWeight / weight;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
            #endregion IValueConverter Members
        }
    }
    

    XAML:

    <Window x:Class="TagCloudDemo.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:TagCloudDemo="clr-namespace:TagCloudDemo"
        Title="Window1" Height="300" Width="300">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <TagCloudDemo:TagCloudControl Tags="{Binding Path=Tags}" Grid.Row="0" Background="Red" />
            <TagCloudDemo:TagCloudControl Words="{Binding Path=Words}" Grid.Row="1" Background="Yellow" />
        </Grid>
    </Window>
    

    后面的代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    
    namespace TagCloudDemo
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
    
                DataContext = new VM();
            }
        }
    
        public class VM
        {
            public VM()
            {
                Words = LoadWords();
                Tags = Tag.CreateTags(Words);
            }
    
            public IEnumerable<string> Words { get; private set; }
    
            public IEnumerable<Tag> Tags { get; private set; }
    
            private static IEnumerable<string> LoadWords()
            {
                Random random = new Random();
    
                string loremIpsum =
                    "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
                string[] tokens = loremIpsum.Split(new char[] {' '});
                List<string> words = new List<string>();
                for (int i = 0; i < 500; i++)
                {
                    words.Add(tokens[random.Next(tokens.Count())]);
                }
                return words;
            }
        }
    
        public class Tag
        {
            public Tag(string name, int weight)
            {
                Name = name;
                Weight = weight;
            }
    
            public string Name { get; set; }
            public int Weight { get; set; }
    
            public static IEnumerable<Tag> CreateTags(IEnumerable<string> words)
            {
                Dictionary<string, int> tags = new Dictionary<string, int>();
                foreach (string word in words)
                {
                    int count = 1;
                    if (tags.ContainsKey(word))
                    {
                        count = tags[word] + 1;
                    }
                    tags[word] = count;
                }
    
                return tags.Select(kvp => new Tag(kvp.Key, kvp.Value));
            }
        }
    }
    

    【讨论】:

    • 您好,谢谢您的回答!由于我是一名新手程序员,是否可以将一些 cmets 特别添加到 VM 类中。再次感谢!
    • VM 将负责为某些模型创建标签和相关权重。该模型将是一些文本内容。 VM 调用 LoadWords() 从模型中加载文本。接下来,它使用字典创建标签来计算重复的数量。
    • 很好的答案,谢谢。有没有办法可以计算控件内的重复数。目的是让一些可重用的东西可以放入任何应用程序中,所以我宁愿不依赖虚拟机。我认为在我最初的问题中,我试图使用 CollectionView 来执行此操作,然后使用 Groups 属性按名称执行分组(因此删除重复项)。你能帮忙吗?再次感谢..
    • 添加一个 IEnumerable 类型的 DP。更改时调用 CreateTags 方法(移动到控件)并填充标签 DP。
    • 您好,感谢您的回复。让这个工作有一些麻烦,不知道我做错了什么。我现在在控件中有 2 个依赖属性 Tag nd String 和 CreateTags 方法我哪里出错了?再次感谢...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    • 1970-01-01
    • 2011-04-07
    • 2010-12-15
    相关资源
    最近更新 更多