Связывание кнопок, созданных во время выполнения с MVVM - PullRequest
0 голосов
/ 10 марта 2020

Моя заявка оформлена следующим образом:

Модели:

Торговля

Просмотры:

TradeManagerView

TradeView <Настраиваемый пользовательский контроль </em>

ViewModels:

TradeManagerViewModel

У TradeManagerView есть DataGrid с ItemsSource, установленным на ObservableCollection<Trade> в TradeManagerViewModel, а его шаблон данных - пользовательский элемент управления TradeView.

Модель Trade имеет свойство TradeDirection, равное In или Out. TradeManagerViewModel представляет два набора ObservableCollection<Button>, по одному на каждый TradeDirection. Эти кнопки настраиваются пользователем во время выполнения, поэтому они не создаются в XAML. Я изо всех сил пытаюсь связать ItemsControl в TradeView с кнопками в TradeManagerViewModel, поскольку его контекст данных является экземпляром Trade, который предоставляется DataGrid ItemsSource.

I для начала попробовал следующее, но ни одна из них не работает. Я использую Caliburn.Micro, который устанавливает DataContext View в ViewModel во время выполнения.

<ItemsControl x:Name="TradeActions" ItemsSource="{Binding InboundTradeActions, ElementName=TradeManager}">

<ItemsControl x:Name="TradeActions" ItemsSource="{Binding InboundTradeActions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">

Как уже упоминалось, мне нужно привязать к правильной коллекции (т.е. InboundTradeActions или OutboundTradeActions) на основе Trade.TradeDirection, поэтому вышеупомянутое не сработает в любом случае. Я не уверен, будет ли это лучше всего работать в XAML с DataTrigger? или создать третье свойство, которое возвращает правильную коллекцию. Но каким-то образом это свойство должно знать TradeDirection для DataContext относительного TradeView.

Надеюсь, все это имеет смысл. Пожалуйста, дайте мне знать, если нет, и я исправлю его.

Торговый класс

internal class Trade : PropertyChangedBase
{
    public enum Direction { In, Out };

    private ObservableCollection<Button> tradeActions = new ObservableCollection<Button>();
    public ObservableCollection<Button> TradeActions
    {
        get { return tradeActions; }
        set
        {
            tradeActions = value; 
            NotifyOfPropertyChange();
        }
    }

    public Direction TradeDirection
    {
        get { return tradeDirection; }
        set
        {
            tradeDirection = value;

            if (value == Direction.In)
            {
                InArrowVisibility = Visibility.Visible;
                OutArrowVisibility = Visibility.Hidden;
            }
            else
            {
                InArrowVisibility = Visibility.Hidden;
                OutArrowVisibility = Visibility.Visible;
            }

            NotifyOfPropertyChange();
        }
    }

    public Visibility InArrowVisibility { get; set; }
    public Visibility OutArrowVisibility { get; set; }

    // Other properties removed to shorten
}

TradeManagerView

<Window x:Class="ExaltedTrade.Views.TradeManagerView"
    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:ExaltedTrade.Views"
    mc:Ignorable="d"
    Title="Trade Manager" Height="450" Width="400"
    WindowStartupLocation="Manual"
    Style="{StaticResource TradeManagerWindow}"
    Top="{Binding WindowTop, Mode=TwoWay}"
    Left="{Binding WindowLeft, Mode=TwoWay}"
    x:Name="TradeManager">
<Grid>
    <DataGrid x:Name="ActiveTrades"
              AutoGenerateColumns="False"
              IsReadOnly="True"
              HeadersVisibility="None"
              GridLinesVisibility="None"
              Style="{StaticResource TradeManagerDataGrid}"
              CellStyle="{StaticResource TradeControlCell}"
              ItemsSource="{Binding ActiveTrades}">
        <DataGrid.Columns>
            <DataGridTemplateColumn Width="*">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <local:TradeView /> <!-- This is the custom UserControl -->
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>
</Window>

TradeView

Я включил иерархию UserControl, но удалил лишние элементы управления для сокращения.

<UserControl x:Class="ExaltedTrade.Views.TradeView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:ExaltedTrade.Views"
         mc:Ignorable="d" 
         d:DesignHeight="150" d:DesignWidth="500" Style="{StaticResource TradeView}">
<Grid>
    <Expander x:Name="Expander" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Top" IsExpanded="True">
        <Expander.Header>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="2*" /><!-- Player Name -->
                    <ColumnDefinition Width="14" /><!-- Trade Direction -->
                    <ColumnDefinition Width="1*" /><!-- Quantity -->
                    <ColumnDefinition Width="16" /><!-- Currency Icon -->
                    <ColumnDefinition Width="26" /><!-- Timer -->
                    <ColumnDefinition Width="26" /><!-- Invite -->
                    <ColumnDefinition Width="26" /><!-- Trade -->
                    <ColumnDefinition Width="26" /><!-- Kick -->
                    <ColumnDefinition Width="26" /><!-- Whisper -->
                    <ColumnDefinition Width="26" /><!-- Close -->
                </Grid.ColumnDefinitions>

                <!-- Some controls removed to shorten -->

        </Expander.Header>
        <Expander.Content>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="2*" />
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="16" />
                    <ColumnDefinition Width="22" />
                </Grid.ColumnDefinitions>

                <!-- Some controls removed to shorten -->

<!-- BINDING IS HERE -->
                <ItemsControl x:Name="TradeActions" Grid.Column="0" Grid.Row="1" ItemsSource="{Binding InboundTradeActions, ElementName=TradeManager}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel Style="{StaticResource TradeActions}" IsItemsHost="True" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </Grid>
        </Expander.Content>
    </Expander>
</Grid>
</UserControl>

TradeManagerViewModel

class TradeManagerViewModel : PropertyChangedBase, IHandle<string>
{

    private ObservableCollection<Trade> activeTrades = new ObservableCollection<Trade>();
    public ObservableCollection<Trade> ActiveTrades
    {
        get
        {
            return activeTrades;
        }
        set
        {
            activeTrades = value;
            NotifyOfPropertyChange();
        }
    }

    public static ObservableCollection<Button> InboundTradeActions { get; set; } = new ObservableCollection<Button>();
    public static ObservableCollection<Button> OutboundTradeActions { get; set; } = new ObservableCollection<Button>();

    public TradeManagerViewModel()
    {
        //TODO: Remove the test Trades
        ObservableCollection<Trade> temp = new ObservableCollection<Trade> { new Trade("ExamplePlayer", Trade.Direction.In) };
        temp.Add(new Trade("AnotherExamplePlayer", Trade.Direction.Out));
        ActiveTrades = temp;

        SetupTradeActions();
    }

    private void SetupTradeActions()
    {
        // TradeActions for inbound Trades
        InboundTradeActions.Clear(); // Remove any existing TradeAction buttons just in case
        foreach (TradeAction tAction in AppSettings.GetSingleton().InboundTradeActions)
        {
            Button action = new Button
            {
                Content = tAction.ButtonText,
                //action.Command = ; //TODO: Need to implement an ICommand to link the method to the buttons
                CommandParameter = tAction
            };

            InboundTradeActions.Add(action);
        }

        // TradeActions for outbound Trades
        OutboundTradeActions.Clear(); // Remove any existing TradeAction buttons just in case
        foreach (TradeAction tAction in AppSettings.GetSingleton().OutboundTradeActions)
        {
            Button action = new Button
            {
                Content = tAction.ButtonText,
                //action.Command = ; //TODO: Need to implement an ICommand to link the method to the buttons
                CommandParameter = tAction
            };

            OutboundTradeActions.Add(action);
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 10 марта 2020

Ваша проблема в команде. Действие в SetupTradeActions, верно? Если это так:

, вы можете определить ICommand в вашей Viewmodel и создать Binding в коде для CommandProperty кнопки. Что-то вроде:

public class myViewModel
{
  public myViewModel()
  {
      InBoundCommand = new InboundCommand ();
      Button button = new Button();
      button.Content = "123";
      Binding b = new Binding("InBoundCommand");
      button.SetBinding(Button.CommandProperty, b);
  }
    public ICommand InBoundCommand {get;}
}

public class InboundCommand : ICommand
{
  public event EventHandler CanExecuteChanged;


  public bool CanExecute(object parameter)
  {
    return true; //or whatever you need here
  }

  public void Execute(object parameter)
  {
    //Do fancy Stuff;
  }
}
0 голосов
/ 10 марта 2020

Использование Style с DataTrigger должно работать. Как то так:

<ItemsControl x:Name="TradeActions" Grid.Column="0" Grid.Row="1">
    <ItemsControl.Style>
        <Style TargetType="ItemsControl">
            <Setter Property="ItemsSource" Value="{Binding DataContext.InboundTradeActions, 
                        RelativeSource={RelativeSource AncestorType=Window}}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding TradeDirection}" Value="Out">
                    <Setter Property="ItemsSource" Value="{Binding DataContext.OutboundTradeActions, 
                                RelativeSource={RelativeSource AncestorType=Window}}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ItemsControl.Style>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel Style="{StaticResource TradeActions}" IsItemsHost="True" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...