如何将新按钮绑定到WPF中的按钮列表
本文关键字:按钮 WPF 列表 绑定 新按钮 | 更新日期: 2023-09-27 18:14:28
我正在尝试动态地将按钮添加到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});
}
我知道语法是错误的,我只是想试着表达我正在思考的代码…一些简单的. .这怎么可能呢?
一个简单的方法是:
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
在处理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;
}
}
}
对于Buttons,你需要一个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 );
}
}
下一类是继承自CommandBase
的RelayCommand
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来保存列表和Add命令
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只是呈现