如何将新按钮绑定到WPF中的按钮列表

时间:2016-10-24 07:22:45

标签: c# wpf

我正在尝试动态添加按钮到WPF中的列表。在做了一些研究之后,我来到了这段代码:

   <Button Content="Add New Button"
        VerticalAlignment="Center" HorizontalAlignment="Center"
        Command="{Binding AddNewButton}"/>
    <ItemsControl ItemsSource="{Binding ButtonsList}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Content="{Binding Title}"
                    Margin="2"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

然后我想在代码上写一些东西以使其工作..因为我发现的所有答案只包括解决方案的XAML,而不是后面的代码。我来自网络背景,所以我对WPF来说是全新的......但我在想这样的事情:

public void AddNewButton(){
 ButtonsList.add({title='New button Title});
}

我知道语法是错误的,我只想尝试表达我正在考虑的代码......简单的事情......这怎么可能有用?

2 个答案:

答案 0 :(得分:1)

一种简单的方法可以是:

<强> XAML:

<Window x:Class="SO40212766.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SO40212766"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Window.CommandBindings>
    <CommandBinding x:Name="AddNewButtonBinding" Command="{x:Static local:MainWindow.AddNewButton}" CanExecute="AddNewButtonBinding_CanExecute" Executed="AddNewButtonBinding_Executed" />
  </Window.CommandBindings>
    <DockPanel>
    <Button Content="Add New Button" DockPanel.Dock="Top"
        VerticalAlignment="Center" HorizontalAlignment="Center"
        Command="{x:Static local:MainWindow.AddNewButton}"/>
    <ItemsControl ItemsSource="{Binding ButtonsList}" DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
      <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
          <WrapPanel IsItemsHost="True"/>
        </ItemsPanelTemplate>
      </ItemsControl.ItemsPanel>
    </ItemsControl>
  </DockPanel>
</Window>

代码背后:

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace SO40212766
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();

      ButtonsList = new ObservableCollection<Button>();
    }

    public static RoutedUICommand AddNewButton = new RoutedUICommand("Add Button", "AddNewButton", typeof(MainWindow));

    public ObservableCollection<Button> ButtonsList { get; private set; }

    private void AddNewButtonBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = true;
    }

    int index = 1;
    private void AddNewButtonBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      // You will of course set the command og click event on the button here...
      ButtonsList.Add(new Button() { Content = $"Button {index++}" });
    }
  }
}

注意从ItemsControl我删除了ItemTemplate。然后,您可以按照自己想要的样式设置按钮的样式。

并注意ItemsControl的DataContext。

答案 1 :(得分:1)

处理WPF时应遵循MVVM模式。

首先,您需要一个基类,它实现了WPF用于绑定通知的INotifyPropertyChanged

using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WpfApplication1
{
    public class ObservableObject : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged( [CallerMemberName] string propertyName = "" )
        {
            var handler = PropertyChanged;
            handler?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
        }

        protected bool SetProperty<T>( ref T backingField, T newValue, [CallerMemberName] string propertyName = "" )
        {
            return SetProperty<T>( ref backingField, newValue, EqualityComparer<T>.Default, propertyName );
        }

        protected bool SetProperty<T>( ref T backingField, T newValue, IEqualityComparer<T> comparer, [CallerMemberName] string propertyName = "" )
        {
            if ( comparer.Equals( backingField, newValue ) ) return false;

            backingField = newValue;
            RaisePropertyChanged( propertyName );
            return true;
        }

        protected bool SetProperty<T>( ref T backingField, T newValue, IComparer<T> comparer, [CallerMemberName] string propertyName = "" )
        {
            if ( comparer.Compare( backingField, newValue ) == 0 ) return false;

            backingField = newValue;
            RaisePropertyChanged( propertyName );
            return true;
        }

    }
}

对于按钮,您需要ICommand,因此我们正在构建一个用于实现

的基类
using System;
using System.Windows.Input;

namespace WpfApplication1
{
    public abstract class CommandBase : ICommand
    {

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

        public virtual bool CanExecute( object parameter )
        {
            return true;
        }

        public void Execute( object parameter )
        {
            if ( CanExecute( parameter ) )
                DoExecute( parameter );
        }

        protected abstract void DoExecute( object parameter );

    }
}

下一课是继承自RelayCommand

CommandBase
using System;

namespace WpfApplication1
{
    public class RelayCommand : CommandBase
    {

        private readonly Func<object, bool> _canexecute;
        private readonly Action<object> _execute;

        public RelayCommand( Action<object> execute ) : this( execute, o => true )
        {
        }

        public RelayCommand( Action<object> execute, Func<object, bool> canexecute )
        {
            if ( execute == null ) throw new ArgumentNullException( nameof( execute ) );
            if ( canexecute == null ) throw new ArgumentNullException( nameof( canexecute ) );

            _execute = execute;
            _canexecute = canexecute;
        }

        public override bool CanExecute( object parameter )
        {
            return base.CanExecute( parameter ) && _canexecute( parameter );
        }

        protected override void DoExecute( object parameter )
        {
            _execute( parameter );
        }

    }
}

现在我们有一个小基地,我们可以继续努力。按钮应执行某些操作并具有将显示的文本。所以我们定义了一个代表这个

的ViewModel类
using System.Windows.Input;

namespace WpfApplication1.ViewModel
{
    public class CommandViewModel : ObservableObject
    {

        private ICommand command;
        private string displayText;

        public ICommand Command
        {
            get { return command; }
            set { SetProperty( ref command, value ); }
        }

        public string DisplayText
        {
            get { return displayText; }
            set { SetProperty( ref displayText, value ); }
        }

    }
}

接下来我们需要一个包含列表的ViewModel和添加命令

using System;
using System.Collections.ObjectModel;

namespace WpfApplication1.ViewModel
{
    public class MainWindowViewModel : ObservableObject
    {

        public MainWindowViewModel()
        {
            AddNewCommand = new CommandViewModel
            {
                DisplayText = "Add",
                Command = new RelayCommand( DoAddNewCommand )
            };

            Commands = new ObservableCollection<CommandViewModel>();
        }

        public CommandViewModel AddNewCommand { get; }

        public ObservableCollection<CommandViewModel> Commands { get; }

        private void DoAddNewCommand( object obj )
        {
            Commands.Add( new CommandViewModel
            {
                DisplayText = "Foo",
                Command = new RelayCommand( DoFoo ),
            } );
        }

        private void DoFoo( object obj )
        {
            throw new NotImplementedException();
        }

    }
}

现在是时候将所有内容绑定在XAML

中了
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        xmlns:vm="clr-namespace:WpfApplication1.ViewModel"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <StackPanel>
            <Button
                Content="{Binding Path=AddNewCommand.DisplayText}"
                Command="{Binding Path=AddNewCommand.Command}"/>

            <ItemsControl
                ItemsSource="{Binding Path=Commands}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel IsItemsHost="True"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button
                            Content="{Binding Path=DisplayText}"
                            Command="{Binding Path=Command}"
                            Margin="2"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Grid>
</Window>

如您所见,根本没有CodeBehind。一切都在ViewModels中完成,而View正在展示。