【问题标题】:WPF User control: can I bind properties from an internal and an external datacontext?WPF 用户控件:我可以绑定来自内部和外部数据上下文的属性吗?
【发布时间】:2024-01-22 06:07:02
【问题描述】:

我正在尝试在 WPF/XAML 中构建我自己的日历控件,既可以作为练习,也可以用于业余项目。此日历将是一个网格,其中每个单元格显然代表所选月份中的一天。每个单元格都应该能够显示项目列表。日历的输入应该是月份的标识和天的列表。那些“天”将是自定义类型,类似于

public class CalendarDay
{
    public int DayNumber {get; set;}
    public List<DayItem> Items {get; set;}
}

DayItem 可以代表约会或待办事项。

我将其作为用户控件来实现。此日历的 XAML 是一个 ControlTemplate,其中包含一个 1x7 的 UniformGrid 用于日期名称(数据绑定到集合 7 个字符串)和一个 6x7 的 UniformGrid 用于日期(数据绑定到 @987654326 的集合@)。

包含此日历的视图(用户控件)在概念上如下所示:

<UserControl name="myView" ... xmlns:cal="clr-namespace:the calendar namespace">

    <Grid>
        <cal:Calendar Days="{Binding DaysWithItems}" CurrentMonth="{Binding DisplayMonth}" />
    </Grid>

</UserControl>

当我应用 MVVM 时,myView 将有一个 DataContext 设置为某个视图模型类,该类具有属性 DaysWithItemsCalenderDay 实例列表)和属性 DisplayMonth

理想情况下,此日历控件的使用者只需提供上述两个输入即可。此外,从myView 的角度来看(双关语纯属巧合),DaysWithItems 应该是一个包含 28、29、30 或 31 个元素的列表,具体取决于月份。这意味着该列表应该以某种方式填充到 42 个项目。我认为这应该是日历控件的责任,而不是myView的视图模型。

请注意,我也没有提供日期名称。这也应该是日历控件的责任。这不应该明确提供。

这是我的问题。如果在日历的控件模板中,我想绑定到日期名称的字符串集合和CalendarDay 的42 个元素集合,则数据上下文应该是Calendar 类本身(因为我之前解释过的职责)。 另一方面,在myView 中,我将日历绑定到myViewDaysWithItems(包含28..31 个元素的(逻辑)集合),所以日历的数据上下文应该是myView的视图模型。

我可以使用某种内部数据上下文(= 控件模板的“内部”)以及某种外部数据上下文(= 日历控件使用者提供的数据上下文)吗?

【问题讨论】:

    标签: wpf xaml mvvm data-binding calendar


    【解决方案1】:

    以下是这类数据绑定的低点。当您想从设置为DataContext 的对象中将数据绑定到属性时,您可以使用普通的Binding Path

    <TextBlock Text="{Binding PropertyOfDataContextObject}" />
    

    但是,当您还想将数据绑定到属性(从在该 UserControl(或 Window)中声明的 UserControl 内部)时,您应该使用 RelativeSource Binding

    <TextBlock Text="{Binding PropertyOfUserControl, RelativeSource={RelativeSource 
        AncestorType={x:Type YourPrefix:YourUserControl}}}" />
    

    您也可以命名一个控件并使用Binding.ElementName Property 来引用它:

    <TextBlock Text="{Binding PropertyOfNamedControl, ElementName=NameOfControl}" />
    

    【讨论】:

      【解决方案2】:

      绑定到 DataContext 以外的其他内容的最简单方法是使用 ElementName

      您可以执行以下操作:

      UserControl.xaml.cs 示例:

      public partial class UserControl1 : UserControl
          {
              public UserControl1()
              {
                  InitializeComponent();
              }
      
      
      
              public int MyProperty
              {
                  get { return (int)GetValue(MyPropertyProperty); }
                  set { SetValue(MyPropertyProperty, value); }
              }
      
              // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
              public static readonly DependencyProperty MyPropertyProperty =
                  DependencyProperty.Register("MyProperty", typeof(int), typeof(UserControl1), new PropertyMetadata(0));
      
      
          }
      

      UserControl.Xaml

      <UserControl x:Class="GridSplitterTest.UserControl1"
                   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" 
                   mc:Ignorable="d" x:Name="MyControl"
                   d:DesignHeight="300" d:DesignWidth="300">
          <Grid>
              <Button Content="{Binding ElementName=MyControl, Path=MyProperty}"></Button>
          </Grid>
      </UserControl>
      

      通过使用 ElementName 并指向 UserControl,您可以绕过 DataContext 并能够绑定到 UC 内的属性。

      【讨论】:

      • 谢谢。我没有想到ElementName。它有效。
      最近更新 更多