WPF 与自定义内容控件上的 DataContext 绑定
WPF Binding with DataContext on Custom Content Control
我有一个自定义向导控件 WizardControl
派生自 UserControl
,它有一个名为 Pages
的依赖项 属性,其数据类型为我的自定义 class名为 WizardPageCollection
.
的集合
WizardControl
托管在具有名为 MainViewModel
的视图模型的 Window
中,向导的页面使用 XAML.
实例化
我正在尝试将页面绑定到子视图模型 Page1VM
和 Page2VM
声明为 MainViewModel
.
上的属性
DataContext
到 Page1VM
的第一页绑定工作正常,但是第二页的绑定失败并显示以下错误消息:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext. BindingExpression:Path=Page2VM; DataItem=null; target element is 'MyPage' (Name=''); target property is 'DataContext' (type 'Object')
问。为什么绑定在第一页上有效,但在第二页上失败,有没有办法让它工作,同时仍然保持 DataContext
XAML 标签内声明的 MainViewModel
MainWindow
?我不想将 ViewModel 用作字典资源,因为这对我们有一些影响,我不会详细介绍。
根据评论者的建议,如果我将绑定更改为使用 RelativeSource,如下所示:
<common:MyPage DataContext="{Binding DataContext.Page1VM, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
<common:MyPage DataContext="{Binding DataContext.Page2VM, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
第一个绑定工作正常,但第二个仍然失败,但出现不同的错误消息(如预期):
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=DataContext.Page2VM; DataItem=null; target element is 'MyPage' (Name=''); target property is 'DataContext' (type 'Object')
感谢您的宝贵时间!
我的代码清单如下:
主窗口XAML:
<Window.DataContext>
<common:MainViewModel />
</Window.DataContext>
<Grid>
<common:WizardControl>
<common:WizardControl.Pages>
<common:WizardPageCollection>
<common:MyPage DataContext="{Binding Page1VM}" />
<common:MyPage DataContext="{Binding Page2VM}" />
</common:WizardPageCollection>
</common:WizardControl.Pages>
</common:WizardControl>
</Grid>
MainViewModel 和 PageViewModel:
public class MainViewModel
{
public PageViewModel Page1VM
{
get;
set;
}
public PageViewModel Page2VM
{
get;
set;
}
public MainViewModel()
{
this.Page1VM = new PageViewModel("Page 1");
this.Page2VM = new PageViewModel("Page 2");
}
}
public class PageViewModel
{
public string Title { get; set; }
public PageViewModel(string title) { this.Title = title; }
}
向导控件XAML:
<Grid>
<ContentPresenter Grid.Row="0" x:Name="contentPage"/>
</Grid>
WizardControl 代码隐藏:
public partial class WizardControl : UserControl
{
public WizardControl()
{
InitializeComponent();
}
public WizardPageCollection Pages
{
get { return (WizardPageCollection)GetValue(PagesProperty); }
set { SetValue(PagesProperty, value); }
}
public static readonly DependencyProperty PagesProperty =
DependencyProperty.Register("Pages", typeof(WizardPageCollection), typeof(WizardControl), new PropertyMetadata(new WizardPageCollection(), new PropertyChangedCallback(Pages_Changed)));
static void Pages_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
WizardPageCollection col = e.NewValue as WizardPageCollection;
WizardControl ctrl = obj as WizardControl;
ctrl.contentPage.Content = col.First();
}
}
public class WizardPageCollection : ObservableCollection<WizardPageBase> { }
public class WizardPageBase : ContentControl { }
我的页面XAML:
<Grid>
<Label Content="{Binding Title}" />
</Grid>
您的方法取决于 Window 的 DataContext
属性 的 value inheritance,它不适用于您的 WizardPageCollection,因为它不构成WPF 元素树。
您应该改为将 MainViewModel 创建为资源,然后通过 StaticResource
:
引用它
<Window ...>
<Window.Resources>
<common:MainViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource MainViewModel}"/>
</Window.DataContext>
<Grid>
<common:WizardControl>
<common:WizardControl.Pages>
<common:WizardPageCollection>
<common:MyPage DataContext="{Binding Page1VM,
Source={StaticResource MainViewModel}}"/>
<common:MyPage DataContext="{Binding Page2VM,
Source={StaticResource MainViewModel}}"/>
</common:WizardPageCollection>
</common:WizardControl.Pages>
</common:WizardControl>
</Grid>
</Window>
@Clemens 回答解决了问题,但问题是别的,恕我直言。
当项目被添加到 WizardPageCollection 时,它也应该被添加到 LogicalTree。查看 ItemsControl 的来源以获取灵感。绝对有可能使您的绑定按原样工作。
我会在这里使用视图模型优先的方法。将页面定义为页面视图模型的集合并生成视图。最后 xaml 看起来像这样:
<common:WizardControl PagesSource="{Binding Pages}">
<common:WizardControl.PageTemplate>
<DataTemplate>
<common:MyPage DataContext="{Binding }" />
</DataTemplate>
</common:WizardControl.PageTemplate>
</common:WizardControl>
或者,考虑您的 WizardControl
派生自 Selector
class 而不是用户控件。 (选择器是来自列表框的基础 class。它有 itemssource 和选定的项目)。
<common:WizardControl ItemsSource="{Binding Pages}"
SelectedItem="{Binding SelectedPage}">
<common:WizardControl.ItemTemplate>
<DataTemplate>
<common:MyPage DataContext="{Binding }" />
</DataTemplate>
</common:WizardControl.ItemTemplate>
</common:WizardControl>
我有一个自定义向导控件 WizardControl
派生自 UserControl
,它有一个名为 Pages
的依赖项 属性,其数据类型为我的自定义 class名为 WizardPageCollection
.
WizardControl
托管在具有名为 MainViewModel
的视图模型的 Window
中,向导的页面使用 XAML.
我正在尝试将页面绑定到子视图模型 Page1VM
和 Page2VM
声明为 MainViewModel
.
DataContext
到 Page1VM
的第一页绑定工作正常,但是第二页的绑定失败并显示以下错误消息:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext. BindingExpression:Path=Page2VM; DataItem=null; target element is 'MyPage' (Name=''); target property is 'DataContext' (type 'Object')
问。为什么绑定在第一页上有效,但在第二页上失败,有没有办法让它工作,同时仍然保持 DataContext
XAML 标签内声明的 MainViewModel
MainWindow
?我不想将 ViewModel 用作字典资源,因为这对我们有一些影响,我不会详细介绍。
根据评论者的建议,如果我将绑定更改为使用 RelativeSource,如下所示:
<common:MyPage DataContext="{Binding DataContext.Page1VM, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
<common:MyPage DataContext="{Binding DataContext.Page2VM, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
第一个绑定工作正常,但第二个仍然失败,但出现不同的错误消息(如预期):
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=DataContext.Page2VM; DataItem=null; target element is 'MyPage' (Name=''); target property is 'DataContext' (type 'Object')
感谢您的宝贵时间!
我的代码清单如下:
主窗口XAML:
<Window.DataContext>
<common:MainViewModel />
</Window.DataContext>
<Grid>
<common:WizardControl>
<common:WizardControl.Pages>
<common:WizardPageCollection>
<common:MyPage DataContext="{Binding Page1VM}" />
<common:MyPage DataContext="{Binding Page2VM}" />
</common:WizardPageCollection>
</common:WizardControl.Pages>
</common:WizardControl>
</Grid>
MainViewModel 和 PageViewModel:
public class MainViewModel
{
public PageViewModel Page1VM
{
get;
set;
}
public PageViewModel Page2VM
{
get;
set;
}
public MainViewModel()
{
this.Page1VM = new PageViewModel("Page 1");
this.Page2VM = new PageViewModel("Page 2");
}
}
public class PageViewModel
{
public string Title { get; set; }
public PageViewModel(string title) { this.Title = title; }
}
向导控件XAML:
<Grid>
<ContentPresenter Grid.Row="0" x:Name="contentPage"/>
</Grid>
WizardControl 代码隐藏:
public partial class WizardControl : UserControl
{
public WizardControl()
{
InitializeComponent();
}
public WizardPageCollection Pages
{
get { return (WizardPageCollection)GetValue(PagesProperty); }
set { SetValue(PagesProperty, value); }
}
public static readonly DependencyProperty PagesProperty =
DependencyProperty.Register("Pages", typeof(WizardPageCollection), typeof(WizardControl), new PropertyMetadata(new WizardPageCollection(), new PropertyChangedCallback(Pages_Changed)));
static void Pages_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
WizardPageCollection col = e.NewValue as WizardPageCollection;
WizardControl ctrl = obj as WizardControl;
ctrl.contentPage.Content = col.First();
}
}
public class WizardPageCollection : ObservableCollection<WizardPageBase> { }
public class WizardPageBase : ContentControl { }
我的页面XAML:
<Grid>
<Label Content="{Binding Title}" />
</Grid>
您的方法取决于 Window 的 DataContext
属性 的 value inheritance,它不适用于您的 WizardPageCollection,因为它不构成WPF 元素树。
您应该改为将 MainViewModel 创建为资源,然后通过 StaticResource
:
<Window ...>
<Window.Resources>
<common:MainViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource MainViewModel}"/>
</Window.DataContext>
<Grid>
<common:WizardControl>
<common:WizardControl.Pages>
<common:WizardPageCollection>
<common:MyPage DataContext="{Binding Page1VM,
Source={StaticResource MainViewModel}}"/>
<common:MyPage DataContext="{Binding Page2VM,
Source={StaticResource MainViewModel}}"/>
</common:WizardPageCollection>
</common:WizardControl.Pages>
</common:WizardControl>
</Grid>
</Window>
@Clemens 回答解决了问题,但问题是别的,恕我直言。
当项目被添加到 WizardPageCollection 时,它也应该被添加到 LogicalTree。查看 ItemsControl 的来源以获取灵感。绝对有可能使您的绑定按原样工作。
我会在这里使用视图模型优先的方法。将页面定义为页面视图模型的集合并生成视图。最后 xaml 看起来像这样:
<common:WizardControl PagesSource="{Binding Pages}"> <common:WizardControl.PageTemplate> <DataTemplate> <common:MyPage DataContext="{Binding }" /> </DataTemplate> </common:WizardControl.PageTemplate> </common:WizardControl>
或者,考虑您的 WizardControl
派生自 Selector
class 而不是用户控件。 (选择器是来自列表框的基础 class。它有 itemssource 和选定的项目)。
<common:WizardControl ItemsSource="{Binding Pages}"
SelectedItem="{Binding SelectedPage}">
<common:WizardControl.ItemTemplate>
<DataTemplate>
<common:MyPage DataContext="{Binding }" />
</DataTemplate>
</common:WizardControl.ItemTemplate>
</common:WizardControl>