简体   繁体   English

WPF MVVM自定义控件问题

[英]WPF MVVM Custom Control issue

I am trying to create a custom Tag Cloud control. 我正在尝试创建自定义标签云控件。 The way I would like it to work is that the user can give it a collection of strings in the itemsSource and the converted string will be displayed in the UI. 我希望它的工作方式是用户可以在itemsSource中给它一个字符串集合,转换后的字符串将显示在UI中。

At the moment what I have is a tag cloud MVVM app which takes a collection strings (containing duplicates) and groups them by “Name” using a collectionView and then a converter is used to decide the FontSize of each string (tag) based on the count of that string.name in the original collection. 目前,我拥有的是标签云MVVM应用程序,该应用程序将收集字符串(包含重复项)并使用collectionView按“名称”将它们分组,然后使用转换器根据其确定每个字符串(标签)的FontSize原始集合中该string.name的计数。

So my question is how can I change this to my desired custom control? 所以我的问题是如何将其更改为所需的自定义控件? I know I will have to extend listbox in my code-behind to expose the itemsSource but the part I am confused about is what to do about the grouping logic (collectionView) as I am currently binding to a grouped collection (no duplicates) whereas I want the user to be able to provide any collection. 我知道我必须在代码后面扩展列表框以暴露itemsSource,但是我困惑的部分是如何处理分组逻辑(collectionView),因为我当前绑定到分组集合(无重复项),而我希望用户能够提供任何集合。 Please let me know if this is clear.? 请让我知道是否清楚。

Grouping logic 分组逻辑

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); }
    }
}
  1. There's no reason for you to extend ListBox and not ItemsControl 您没有理由扩展ListBox而不是ItemsControl

  2. Why are you going the Custom Control path and not the User Control Path? 为什么要选择“自定义控制”路径而不是“用户控制”路径?

What I would do is define a UserControl with a Dependency Property of IEnumerable Tags. 我要做的是定义一个IEnumerable标签的Dependency属性的UserControl。 I would then bind this DP to the ViewModel (though there's really no need to use MVVM inside controls, but that's really up to you...) and then the ViewModel can go through those strings, and expose another Property - ObservableCollection Tags, Where Tag would be something like this: 然后,我将这个DP绑定到ViewModel(尽管实际上不需要控件使用MVVM,但这实际上取决于您...),然后ViewModel可以遍历这些字符串,并公开另一个Property-ObservableCollection Tags,标签将是这样的:

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

}

The XAML would contain just an ItemsControl, that would be databound to the list of Tags in the ViewModel. XAML将只包含一个ItemsControl,它将被数据绑定到ViewModel中的Tag列表。 This way it would work exactly like you want. 这样,它将完全像您想要的那样工作。

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

Below is some sample code which I believe does what you are looking for. 以下是一些示例代码,我相信它们可以满足您的需求。 Max font size is hard coded to 32 but should really be another Dependency Property. 最大字体大小被硬编码为32,但实际上应该是另一个依赖项属性。

Edit: The control now have DPs for both Tags and Words collections. 编辑:控件现在具有用于标签和单词集合的DP。

XAML: 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>

Code behind: 后面的代码:

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: 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>

Code behind: 后面的代码:

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));
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM