WPF与自定义内容控件上的DataContext绑定

时间:2015-08-12 09:24:21

标签: wpf xaml contentcontrol contentpresenter

我有一个自定义向导控件Content-type: application/json; charset=utf-8派生自WizardControl,它有一个名为UserControl的依赖项属性,其数据类型为我的自定义类集合Pages

WizardPageCollection托管在WizardControl中,其视图模型名为Window,向导的页面使用XAML进行实例化。

我正在尝试将网页绑定到MainViewModel上声明为属性的子视图模型Page1VMPage2VM

MainViewModelDataContext的第一页绑定工作正常,但第二页的绑定失败,并显示以下错误消息:

Page1VM

Q值。为什么绑定在第一页上起作用但在第二页上失败并且有一种方法可以让它工作,同时仍然保持在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') MainViewModel {1}}的{​​{1}} XAML标记内声明DataContext ?我不希望将ViewModel用作字典资源,因为这对我们有一些影响,我不会详细介绍。

根据评论员的建议,如果我更改绑定以使用RelativeSource,如下所示:

MainWindow

第一个绑定工作正常,但第二个绑定仍然失败,但是有一个不同的错误消息(正如预期的那样):

<common:MyPage DataContext="{Binding DataContext.Page1VM, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
<common:MyPage DataContext="{Binding DataContext.Page2VM, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

谢谢你的时间!

我的代码清单如下所示:

MainWindow XAML:

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')

MainViewModel和PageViewModel:

<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>

WizardControl XAML:

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; }
}

WizardControl代码隐藏:

<Grid>
    <ContentPresenter Grid.Row="0" x:Name="contentPage"/>
</Grid>

MyPage XAML:

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 { }

2 个答案:

答案 0 :(得分:1)

您的方法取决于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>

答案 1 :(得分:0)

@Clemens回答问题的解决方法,但问题是别的,imho。

  1. 当项目添加到WizardPageCollection时,它也应该添加到LogicalTree中。查看ItemsControl的来源以获取灵感。确定你的装订工作原样是可能的。

  2. 我会在这里使用viewmodel第一种方法。将页面定义为页面视图模型的集合并生成视图。最后,xaml看起来像这样:

    <common:WizardControl PagesSource="{Binding Pages}">
        <common:WizardControl.PageTemplate>
            <DataTemplate>
                <common:MyPage DataContext="{Binding }" />
            </DataTemplate>
        </common:WizardControl.PageTemplate>
    </common:WizardControl>
    
  3. 另外,请考虑从WizardControl类派生Selector而不是usercontrol。 (Selector是listbox的基类。它有itemssource和selected item。)

        <common:WizardControl ItemsSource="{Binding Pages}" 
                              SelectedItem="{Binding SelectedPage}">
            <common:WizardControl.ItemTemplate>
                <DataTemplate>
                    <common:MyPage DataContext="{Binding }" />
                </DataTemplate>
            </common:WizardControl.ItemTemplate>
        </common:WizardControl>