2016-10-24 3 views
0

Я пытаюсь динамически добавлять кнопки в список в 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}); 
} 

Я знаю, что синтаксис не так, я просто хотел, чтобы попытаться выразить код, я думать о .. что-то простое .. Как это могло сработать?

+0

Когда вы добавляете элемент в ButtonList, ItemControl создаст для вас кнопку (потому что это кнопка в ItemTemplate).Но ButtonList лучше должен быть 'ObservableCollection <>', а не простой 'List <>', потому что ObservableCollection <> 'уведомляет пользовательский интерфейс, когда элементы были изменены – ASh

+0

, поэтому я бы объявил ButtonList как ObservableCollecition? – raphadko

+0

хорошо, да, это certanly не причинит вреда. Интерфейс ObservableCollecition и INotifyPropertyChanged - это две основные вещи WPF, чтобы уведомить пользовательский интерфейс WPF о любых изменениях в ваших данных – ASh

ответ

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. Затем вы можете стилизовать кнопки в стиле так, как вы хотите.

И помните о DataContext элемента ItemsControl.

1

Вы должны следовать шаблону MVVM при работе с WPF.

Прежде всего, необходимо базовый класс, который реализует INotifyPropertyChanged, который используется WPF для связывания уведомления:

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, и представление просто представляет.

Смежные вопросы