自定义数据绑定到不止一件事 WPF C#

Custom Data Binding to more than one thing WPF C#

我有一个 XAML 文件,其中包含两个内容,

  1. 一个组合框

  2. 一个堆栈面板

如何使组合框中的更改自动使堆栈面板从一个堆栈面板切换到另一个堆栈面板。

我的组合框类似于

<ComboBox x:Name="MCbConnect" SelectedIndex="{Binding EnConnectionType}" Loaded="m_cbConnect_Loaded" SelectionChanged="m_cbConnect_SelectionChanged" Width="100"></ComboBox>

其中 EnConnectionType 是这样的 属性

private ConnectionType _enConnectionType;
    public ConnectionType EnConnectionType
    {
        get { return _enConnectionType; }
        set { SetProperty(ref _enConnectionType, value, "EnConnectionType");  }
    }

ConnectionType 是

public enum ConnectionType { Rs232 = 0, Can = 1, Ethernet = 2 };

所以我已经在这里实现了 INotifyChanged 接口。但我不知道如何将这些数据与 stackpanel 容器绑定,这将允许我在后台自动切换到不同的 stackpanel 视图。

我想切换到的 XAML 示例是

<GroupBox x:Class="Gui.CtrlCommSocketSettings"
    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:src="clr-namespace:Akribis.Gui"              
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    Header="Comm Settings"
    mc:Ignorable="d" 
    d:DesignHeight="80" d:DesignWidth="300">

<Grid Height="70" VerticalAlignment="Top">
    <Grid.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="0,0,3,0"/>
        </Style>

        <Style TargetType="TextBox">
            <Setter Property="Width" Value="120"/>
            <Setter Property="Margin" Value="0,1"/>
        </Style>

        <Style TargetType="CheckBox">
            <Setter Property="Margin" Value="0,4"/>
        </Style>
    </Grid.Resources>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <TextBlock Grid.Row="0" Text="Server:" Name="MTextBlockServer"/>
    <TextBlock Grid.Row="1" Text="Port:"/>

    <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Server}" Name="MTextBoxServer"/>
    <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Port}"/>
</Grid>

带有类似

的 cs 文件
namespace Gui
{
    public partial class CtrlCommSocketSettings 
    {
        public CtrlCommSocketSettings()
        {
            InitializeComponent();
        }
    }
}

我不想以编程方式执行此操作,因为我知道我想避免模型和视图之间的耦合。

我不想做但现在有的例子: 在主要 XAML 中,我有一个空的堆栈面板

<StackPanel Orientation="Vertical" Name="MCtrlCommSettings"></StackPanel> 

并且我通过

之类的操作明确地添加到此堆栈面板
MCtrlCommSettings.Children.Clear();
MCtrlCommSettings.Children.Add(_serverCtrlCommSettings);

我该如何自动执行此操作?就像 InotifyChanges 将如何在视图和模型之间自动更新一样。任何建议都将受到欢迎。

在线阅读,看来我需要实现某种可观察列表

我建议为每种连接类型使用 DataTemplates 和单独的 ViewModels。只需使用每个 ViewModel 的目标类型指定 DataTemplates,然后使用 ContentControl,内容 属性 绑定主视图模型的 CurrentConnection 属性,这取决于 ConnectionType 组合框的 SelectedValue。

更新
源代码说明解决方案:

XAML

<Window x:Class="MVVMExample.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:MVVMExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="425">
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:Rs232ConnectionViewModel}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>

                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>

                <TextBlock Grid.Row="0" Text="Rs232Port:" />

                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Rs232Port}" />
            </Grid>

        </DataTemplate>

        <DataTemplate DataType="{x:Type local:CanConnectionViewModel}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>

                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>

                <TextBlock Grid.Row="0" Text="CanParam:" />

                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding CanParam}" />
            </Grid>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:EthernetConnectionViewModel}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>

                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>

                <TextBlock Grid.Row="0" Text="Server:" />
                <TextBlock Grid.Row="1" Text="Port:"/>

                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding EthernetServer}" />
                <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding EthernetPort}"/>
            </Grid>
        </DataTemplate>
    </Window.Resources>
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ComboBox x:Name="MCbConnect" SelectedValue="{Binding CurrentConnectionType}" ItemsSource="{Binding ConnectionTypes}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding ConnectionType}" />
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>

        <ContentControl Grid.Row="1" Content="{Binding CurrentConnectionType}" />


    </Grid>
</Window>

C#

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

public class MainWindowViewModel : INotifyPropertyChanged
{
    ObservableCollection<ConnectionTypeViewModel> _connectionTypes;

    public ObservableCollection<ConnectionTypeViewModel> ConnectionTypes
    {
        get { return _connectionTypes; }
        private set { _connectionTypes = value; }
    }

    public MainWindowViewModel()
    {
        ConnectionTypes = new ObservableCollection<ConnectionTypeViewModel>(new ConnectionTypeViewModel[] 
        {
            new Rs232ConnectionViewModel() { ConnectionType = ConnectionType.Rs232, Rs232Port="COM1"},
            new CanConnectionViewModel() { ConnectionType = ConnectionType.Can},
            new EthernetConnectionViewModel() { ConnectionType = ConnectionType.Ethernet, EthernetServer="tcp://xxxx"},
        });

        CurrentConnectionType = ConnectionTypes[2];
    }

    private ConnectionTypeViewModel _currentConnectionType;
    public ConnectionTypeViewModel CurrentConnectionType
    {
        get { return _currentConnectionType; }
        set
        {
            _currentConnectionType = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentConnectionType)));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}


public class ConnectionTypeViewModel : INotifyPropertyChanged
{
    private ConnectionType _connectionTypeName;

    public ConnectionType ConnectionType
    {
        get { return _connectionTypeName; }
        set { _connectionTypeName = value; OnPropertyChanged(); }
    }

    protected void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}


public class Rs232ConnectionViewModel : ConnectionTypeViewModel
{
    private string _rs232Port;

    public string Rs232Port
    {
        get { return _rs232Port; }
        set { _rs232Port = value; OnPropertyChanged(); }
    }
}


public class CanConnectionViewModel : ConnectionTypeViewModel
{
    private string _canParam;
    public string CanParam
    {
        get { return _canParam; }
        set { _canParam = value; OnPropertyChanged(); }
    }
}

public class EthernetConnectionViewModel : ConnectionTypeViewModel
{
    private string _ethernetServer;
    public string EthernetServer
    {
        get { return _ethernetServer; }
        set { _ethernetServer = value; OnPropertyChanged(); }
    }

    private string _ethernetPort;
    public string EthernetPort
    {
        get { return _ethernetPort; }
        set { _ethernetPort = value; OnPropertyChanged(); }
    }
}

public enum ConnectionType { Rs232 = 0, Can = 1, Ethernet = 2 };