【问题标题】:wpf xaml MVVM inheritance with multiple ContentPresenter具有多个 ContentPresenter 的 wpf xaml MVVM 继承
【发布时间】:2022-10-07 15:34:53
【问题描述】:

我正在重写有很多共同点的导入掩码,所以我想(并且必须)使用继承。

我有一个包含所有常用控件的基本 UserControl:(我省略了网格定义)

基类.xaml

<UserControl x:Class="BaseImport.BaseClass"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <UserControl.Template>
    <ControlTemplate TargetType="UserControl">
      <Grid>
        <Border Grid.Row="0" Grid.Column="0">
          <StackPanel>
            <Label Content="Text1:"/>
            <ComboBox Name="cbText1" MinWidth="80"/>
          </StackPanel>
        </Border>
        
        <Border Grid.Row="0" Grid.Column="1">
          <StackPanel>
            <Label Content="Text2:"/>
            <ComboBox Name="cbText2" MinWidth="80"/>
          </StackPanel>
        </Border>
        
        <Border Grid.Row="0" Grid.Column="2">
          <StackPanel>
            <ContentPresenter ContentSource="Content"/> <!-- ContentSource="Content" is the default-->
          </StackPanel>
        </Border>

        <!-- next Row -->
        <Border Grid.Row="1" Grid.Column="0">
          <StackPanel>
            <Label Content="Text3:"/>
            <TextBox Name="tbText3" TextWrapping="Wrap" Text="" MinWidth="80" VerticalAlignment="Center"/>
          </StackPanel>
        </Border>

        <Border Grid.Row="1" Grid.Column="1">
          <StackPanel>
            <ContentPresenter/> 
          </StackPanel>
        </Border>
      </Grid>
    </ControlTemplate>
  </UserControl.Template>
</UserControl>

这是一种像这样“使用”的模板:

MainWindow.xaml(仅用于演示主窗口)

<Window x:Class="zzz.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:my="clr-namespace:BaseImport;assembly=BaseImport"
        mc:Ignorable="d"
        Title="MainWindow" Height="280" Width="600">
  <my:BaseClass>
    <StackPanel>
      <Label Content="Test:"/>
      <ComboBox ItemsSource="{Binding TestTyps}" MinWidth="80"/>
    </StackPanel>
  </my:BaseClass>
</Window>

主窗口.xaml.cs

using WpfApp1.ViewModel;

namespace zzz
{
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();

      this.DataContext = new MainViewModel();
    }
  }
}

并将它包装起来 MainViewModel.cs:

namespace WpfApp1.ViewModel
{
  public class MainViewModel : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler? PropertyChanged;

    public string[] TestTyps { get { return new string[] { "abc", "123", "xyz" }; } }

  }
}

如果我有一个 ContentPresenter 一切正常。但在 BaseClass 中,我有两个,可能更多。 像这样只有“最后一个”演示者被填充。而在 MainWindow.xaml 中只能声明一个。

如何在 MainWindow.xaml 中添加更多内容?

我怎样才能选择合适的?

谢谢

红色矩形是第二个演示者所在的位置(第 1 行,第 1 列),但我希望它是箭头点(第 0 行,第 2 列)。

我想要另一个控件来代替也在 MainWindow.xaml 中声明的红色矩形。

【问题讨论】:

  • 如果您添加一张带有您想要为new string[] { "abc", "123", "xyz" } 实现的用户界面的图片,这将有所帮助。通过重复标记判断,您可以在某处使用 ItemsControl
  • @David:当控件只有一个Content 属性时,为什么模板中有多个ContentPresenter?你应该如何设置“其他”内容?

标签: c# wpf xaml mvvm


【解决方案1】:

您直接放在&lt;my:BaseClass&gt; 标签中的内容是 contentProperty,它只能是一个。

为什么只有最后一个 ContentPresenter 显示它?因为每个 VisualElement 只能有一个父级,所以最后一个声称它的父级获胜。

但是,您可以根据需要创建任意数量的属性。

<UserControl x:Class="BaseImport.BaseClass"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <UserControl.Template>
    <ControlTemplate TargetType="UserControl">
      <Grid>
        <Border Grid.Row="0" Grid.Column="0">
          <StackPanel>
            <Label Content="Text1:"/>
            <ComboBox Name="cbText1" MinWidth="80"/>
          </StackPanel>
        </Border>
        
        <Border Grid.Row="0" Grid.Column="1">
          <StackPanel>
            <Label Content="Text2:"/>
            <ComboBox Name="cbText2" MinWidth="80"/>
          </StackPanel>
        </Border>
        
        <Border Grid.Row="0" Grid.Column="2">
          <StackPanel>
            <ContentPresenter ContentSource="FirstContent"/>
          </StackPanel>
        </Border>

        <!-- next Row -->
        <Border Grid.Row="1" Grid.Column="0">
          <StackPanel>
            <Label Content="Text3:"/>
            <TextBox Name="tbText3" TextWrapping="Wrap" Text="" MinWidth="80" VerticalAlignment="Center"/>
          </StackPanel>
        </Border>

        <Border Grid.Row="1" Grid.Column="1">
          <StackPanel>
            <ContentPresenter ContentSource="SecondContent"/> 
          </StackPanel>
        </Border>
      </Grid>
    </ControlTemplate>
  </UserControl.Template>
</UserControl>

<my:BaseClass>
  <my:BaseClass.FirstContent>
    <StackPanel>
      <Label Content="Test:"/>
      <ComboBox ItemsSource="{Binding TestTyps}" MinWidth="80"/>
    </StackPanel>
  <my:BaseClass.FirstContent>
  <my:BaseClass.SecondContent>
    <StackPanel>
      <Label Content="Test10:"/>
      <TextBox Text="{Binding Whatever}" MinWidth="80"/>
    </StackPanel>
  <my:BaseClass.SecondContent>
</my:BaseClass>

【讨论】:

    【解决方案2】:

    我把重点给了 Firo,因为他给了我正确的答案。但我想给出一些最后的想法,以便每个人都可以从中受益。

    对于 xaml“基类”:

        <UserControl 
          x:Class="BaseImport.BaseClass"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
          <UserControl.Template>
            <ControlTemplate TargetType="UserControl">
              <Grid>
                <Border Grid.Row="0" Grid.Column="0">
                  <StackPanel>
                    <Label Content="Text1:"/>
                    <ComboBox Name="cbText1" MinWidth="80"/>
                  </StackPanel>
                </Border>
        
                <Border Grid.Row="0" Grid.Column="1">
                  <StackPanel>
                    <Label Content="DB:"/>
                    <ComboBox 
                      Name="cbxDB" 
                      ItemsSource="{Binding DBs}" 
                      SelectedItem="{Binding DB}" 
                      MinWidth="80" 
                      SelectionChanged="selectionChanged"
                      Loaded="DB_Loaded">
                      <!--
                      here the DataContext can be set differently
                      <ComboBox.DataContext>
                        <Views:DBViewModel/>
                      </ComboBox.DataContext>
                      -->
                    </ComboBox>
                  </StackPanel>
                </Border>
              </Grid>
            </ControlTemplate>
          </UserControl.Template>
        </UserControl>
    

    对于“基类”:

        public partial class BaseClass : UserControl
        {
          public BaseClass(BaseViewModel _bvm)
          {
            InitializeComponent();
            // set DataContext for all other controls
            DataContext = _bvm;
          }
        }
    

    对于“派生”类 xaml:

        <UserControl x:Class="WpfApp1.DerivedClass"
                     xmlns:base="clr-namespace:BaseImport.Views;assembly=BaseImport"
                     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">
          <StackPanel>
            <base:BaseClass Name="bc">
              <base:BaseClass.ContentOneTemplate>
                <DataTemplate>
                  <StackPanel>
                    <Label Content="Test:"/>
                    <ComboBox
                      ItemsSource="{Binding TestTyps}"
                      SelectedItem="{Binding TestTyp}"
                      SelectionChanged="TestTypChanged"
                      Loaded="TestTypLoaded">
                      <!--
                      here the DataContext can be set differently
                      <ComboBox.DataContext>
                        <Views:ViewModelXYZ/>
                      </ComboBox.DataContext>
                      -->
                    </ComboBox>
                  </StackPanel>
                </DataTemplate>
              </base:BaseClass.ContentOneTemplate>
            </base:BaseClass>
          </StackPanel>
        </UserControl>
    

    对于“派生”类:

        public partial class DerivedClass : UserControl
        {
          ViewModelABC vmabc = null;
           
          public DerivedClass(ViewModel _vm) : this()
          {
            vmabc = _vm;
            this.DataContext = vmabc;
            bc.DataContext = vmabc;  // or another viewmodel that holds TestTyps and TestTyp or leave it for ViewModelXYZ
          }
          
          private void TestTypChanged(object sender, SelectionChangedEventArgs e)
          {
            PropertyChanged();
          }
      
          private void TestTypLoaded(object sender, RoutedEventArgs e)
          {
            (sender as ComboBox).SelectedValue = (this.DataContext as ViewModel(XYZ/ABC).TestTyp;
          }
        }
    

    这就是我想出来的。

    我希望这对其他人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-27
      • 2013-09-20
      • 2013-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多