如何在组合框中绑定多个按钮 [英] How to bind Multiple Buttons in Combobox

查看:55
本文介绍了如何在组合框中绑定多个按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我想在组合框中绑定按钮列表。每个组合框将包含一个类别的按钮,依此类推。如下图所示。



下面是我的代码:

 < ItemsControl x:Name = iNumbersList> 
< ItemsControl.ItemsPanel>
< ItemsPanelTemplate>
< WrapPanel Horizo​​ntalAlignment = Center VerticalAlignment = Center Orientation = Horizo​​ntal MaxWidth = 930 />
< / ItemsPanelTemplate>
< /ItemsControl.ItemsPanel>
< ItemsControl.ItemTemplate>
< DataTemplate>
< Button Click = ItemButtonClick
Tag = {Binding ItemTag}
Horizo​​ntalContentAlignment = Center
VerticalContentAlignment = Center
Height = 100 Width = 300>
< TextBlock TextAlignment = Center Foreground = Red
Horizo​​ntalAlignment = Center FontWeight = SemiBold
FontSize = 25 TextWrapping = Wrap
Text = {Binding ItemDisplayMember} />
< / Button>
< / DataTemplate>
< /ItemsControl.ItemTemplate>



 公共类NumberModel 
{
公共字符串ItemDisplayMember {get;组; }
公共对象ItemTag {get;组; }
公用字串ItemCategory {get;组; }
}

如何按 ItemCategory分组属性并将其绑定到GUI,是否为每个 ItemCategory ComboBox 绑定,然后在其中单击多个按钮? / p>

解决方案

可能您不需要 ComboBox ,但



PS啊,是的,我忘了给你看代码隐藏课程。



MainWindow.xaml.cs

 公共部分类MainWindow:窗口
{
public MainWindow()
{
InitializeComponent();
}
}


I want to bind a list of buttons in combo boxes. Each Combobox will contains buttons of one category and so on. As referred in attached image.

Below is my code:

<ItemsControl x:Name="iNumbersList">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal" MaxWidth="930"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button  Click="ItemButtonClick"
                 Tag="{Binding ItemTag}"
                 HorizontalContentAlignment="Center"
                 VerticalContentAlignment="Center"
                 Height="100" Width="300">
                <TextBlock TextAlignment="Center" Foreground="Red"
                       HorizontalAlignment="Center" FontWeight="SemiBold"
                       FontSize="25" TextWrapping="Wrap"
                       Text="{Binding ItemDisplayMember}"/>
            </Button>
        </DataTemplate>
    </ItemsControl.ItemTemplate>

public class NumberModel
{
    public string ItemDisplayMember { get; set; }
    public object ItemTag { get; set; }
    public string ItemCategory { get; set; }
}

How can I group by ItemCategory property and bind it on GUI, a ComboBox for each ItemCategory and then multiple buttons in it?

解决方案

Probably you don't need a ComboBox but Expander because the objective is reachable using it. ComboBox is needed when you have to filter something inside it or use dropdown displayed above Windows content.

I wrote a simple example using MVVM programming pattern. There will be many new classes but most of it you need add to the project only once. Let's go from the scratch!

1) Create class NotifyPropertyChanged to implement INotifyPropertyChanged interface. It needed to make able Binding to update the layout dynamically in Runtime.

NotifyPropertyChanged.cs

public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

2) Create MainViewModel class derived from NotifyPropertyChanged. It will be used for Binding target Properties.

MainViewModel.cs

public class MainViewModel : NotifyPropertyChanged
{
    public MainViewModel()
    {

    }
}

3) Attach MainViewModel to MainWindow's DataContext. One of ways - doing it in xaml.

MainWindow.xaml

<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>

4) Derive the data class NumberModel from NotifyPropertyChanged and add OnPropertyChanged call for each Property. This will give wonderful effect: when you change any Property in runtime, You'll immediately see the changes in UI. MVVM Magic called Binding :)

NumberModel.cs

public class NumberModel : NotifyPropertyChanged
{
    private string _itemDisplayMember;
    private object _itemTag;
    private string _itemCategory;

    public string ItemDisplayMember
    { 
        get => _itemDisplayMember;
        set 
        {
            _itemDisplayMember = value;
            OnPropertyChanged();
        }
    }
    public object ItemTag
    {
        get => _itemTag;
        set
        {
            _itemTag = value;
            OnPropertyChanged();
        }
    }
    public string ItemCategory
    {
        get => _itemCategory;
        set
        {
            _itemCategory = value;
            OnPropertyChanged();
        }
    }
}

5) When button is clicked I will not handle the Click event but call a Command. For easy use of Commands I suggest this relaying its logic class (grabbed here).

RelayCommand.cs

public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
    public void Execute(object parameter) => _execute(parameter);
}

6) All ready to fill the MainViewModel with code. I've added there a command and some items to collection for test.

MainViewModel.cs

public class MainViewModel : NotifyPropertyChanged
{
    private ObservableCollection<NumberModel> _itemsList;
    private ICommand _myCommand;

    public ObservableCollection<NumberModel> ItemsList
    {
        get => _itemsList;
        set
        {
            _itemsList = value;
            OnPropertyChanged();
        }
    }

    public ICommand MyCommand => _myCommand ?? (_myCommand = new RelayCommand(parameter =>
    {
        if (parameter is NumberModel number) 
            MessageBox.Show("ItemDisplayMember: " + number.ItemDisplayMember + "\r\nItemTag: " + number.ItemTag.ToString() + "\r\nItemCategory: " + number.ItemCategory);
    }));

    public MainViewModel()
    {
        ItemsList = new ObservableCollection<NumberModel>
        {
            new NumberModel { ItemDisplayMember = "Button1", ItemTag="Tag1", ItemCategory = "Category1" },
            new NumberModel { ItemDisplayMember = "Button2", ItemTag="Tag2", ItemCategory = "Category1" },
            new NumberModel { ItemDisplayMember = "Button3", ItemTag="Tag3", ItemCategory = "Category1" },
            new NumberModel { ItemDisplayMember = "Button4", ItemTag="Tag4", ItemCategory = "Category2" },
            new NumberModel { ItemDisplayMember = "Button5", ItemTag="Tag5", ItemCategory = "Category2" },
            new NumberModel { ItemDisplayMember = "Button6", ItemTag="Tag6", ItemCategory = "Category2" },
            new NumberModel { ItemDisplayMember = "Button7", ItemTag="Tag7", ItemCategory = "Category3" },
            new NumberModel { ItemDisplayMember = "Button8", ItemTag="Tag8", ItemCategory = "Category4" },
            new NumberModel { ItemDisplayMember = "Button9", ItemTag="Tag9", ItemCategory = "Category4" }
        };
    }
}

7) The main answer to your main question is: use IValueConverter to filter the list with requred criteria. I wrote 2 converters. First for Categories, second for Buttons.

Converters.cs

public class CategoryConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is ObservableCollection<NumberModel> collection)
        {
            List<NumberModel> result = new List<NumberModel>();
            foreach (NumberModel item in collection)
            {
                if (!result.Any(x => x.ItemCategory == item.ItemCategory))
                    result.Add(item);
            }
            return result;
        }
        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class ItemGroupConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] is ObservableCollection<NumberModel> collection && values[1] is string categoryName)
        {
            List<NumberModel> result = new List<NumberModel>();
            foreach (NumberModel item in collection)
            {
                if (item.ItemCategory == categoryName)
                    result.Add(item);
            }
            return result;
        }
        return null;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

8) Now everything is ready to fill the markup. I post full markup here to make everything clear.

Note: I faced Visual Studio 2019 16.5.4 crash while setting a MultiBinding in ItemsSource and applied the workaround.

MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp1"
        Title="MainWindow" Height="600" Width="1000" WindowStartupLocation="CenterScreen">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Window.Resources>
        <local:CategoryConverter x:Key="CategoryConverter"/>
        <local:ItemGroupConverter x:Key="ItemGroupConverter"/>
    </Window.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding ItemsList, Converter={StaticResource CategoryConverter}}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Vertical"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="{x:Type local:NumberModel}">
                    <Expander Header="{Binding ItemCategory}">
                        <ItemsControl DataContext="{Binding DataContext,RelativeSource={RelativeSource AncestorType=Window}}">
                            <ItemsControl.Style>
                                <Style TargetType="ItemsControl">
                                    <Setter Property="ItemsSource">
                                        <Setter.Value>
                                            <MultiBinding Converter="{StaticResource ItemGroupConverter}">
                                                <Binding Path="ItemsList"/>
                                                <Binding Path="Header" RelativeSource="{RelativeSource AncestorType=Expander}"/>
                                            </MultiBinding>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </ItemsControl.Style>
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <WrapPanel HorizontalAlignment="Left" VerticalAlignment="Center" Orientation="Horizontal" MaxWidth="930"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate DataType="{x:Type local:NumberModel}">
                                    <Button Tag="{Binding ItemTag}"
                                            HorizontalContentAlignment="Center"
                                            VerticalContentAlignment="Center"
                                            Height="100" Width="300"
                                            Command="{Binding DataContext.MyCommand,RelativeSource={RelativeSource AncestorType=Window}}"
                                            CommandParameter="{Binding}">
                                        <TextBlock TextAlignment="Center" Foreground="Red"
                                                   HorizontalAlignment="Center" FontWeight="SemiBold"
                                                   FontSize="25" TextWrapping="Wrap"
                                                   Text="{Binding ItemDisplayMember}"/>
                                    </Button>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </Expander>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>

Job is done with MVVM. Enjoy. :)

P.S. Ah, yes, I forgot to show you the code-behind class. Here it is!

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

这篇关于如何在组合框中绑定多个按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆