WPF MVVM自定义控件问题

时间:2011-02-25 10:42:54

标签: c# wpf

我正在尝试创建自定义Tag Cloud控件。我希望它的工作方式是用户可以在itemsSource中为它提供一组字符串,转换后的字符串将显示在UI中。

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

所以我的问题是如何将其更改为我想要的自定义控件?我知道我必须在我的代码隐藏中扩展listbox以暴露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); }
    }
}

2 个答案:

答案 0 :(得分:1)

  1. 你没有理由扩展ListBox而不是ItemsControl

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

  3. 我要做的是定义一个具有IEnumerable标签的依赖属性的UserControl。 然后我将这个DP绑定到ViewModel(尽管实际上不需要在控件内部使用MVVM ,但这完全取决于你......)然后ViewModel可以遍历这些字符串并公开另一个财产 - ObservableCollection标签, 标签会是这样的:

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

    XAML只包含一个ItemsControl,它将被数据绑定到ViewModel中的Tags列表。这样就可以完全按照你想要的方式工作。

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

答案 1 :(得分:0)

以下是一些示例代码,我相信您正在寻找的代码。 Max字体大小硬编码为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));
        }
    }
}