我正在尝试动态添加按钮到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});
}
我知道语法是错误的,我只想尝试表达我正在考虑的代码......简单的事情......这怎么可能有用?
答案 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正在展示。