简体   繁体   中英

chaining dependency properties xaml wpf

I have a usercontrol that I wish to have access through its parent xaml ( in this case a wpf page ). my user control has a label property, that i wish to set through the page xaml by binding it to the datacontext that is passed into the page xaml.

At the moment in the example below the databinding from the page xaml appears to be working correctly. I can set the content of the user controls labels through the page xaml as long as im not attempting to bind it to the pages datacontext, when I do attempt this, the label always appears blank.

Ive followed a number of other related examples suggested on this site, but so far have yet to pinpoint where i'm going wrong. Any advice would be greatly appreciated.

UserControl XAML

<UserControl x:Class="Pipeline_General.Custom_Controls.AttributeStack"
         xmlns:pm="clr-namespace:Pipeline_General"
         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"
         DataContext = "{Binding RelativeSource={RelativeSource Self}}"
         d:DesignHeight="30" d:DesignWidth="300">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*" />
        <ColumnDefinition Width="3*" />
    </Grid.ColumnDefinitions>
    <Label Foreground="{x:Static pm:myBrushes.pink}" Content="{Binding Path=Attr}"  Grid.Column="0" HorizontalAlignment="Right" FontFamily="Calibri" FontSize="14" Margin="5,0,5,0"/>
    <Label x:Name="ValLabel" Foreground="{x:Static pm:myBrushes.blue}" Grid.Column="1" HorizontalAlignment="Left" FontFamily="Calibri" FontSize="14" Margin="5,0,5,0"/>
</Grid>

UserControl Code Behind

public partial class AttributeStack : UserControl
{


    public static readonly DependencyProperty AttrProperty = DependencyProperty.Register
        (
             "Attr",
             typeof(string),
             typeof(AttributeStack),
             new PropertyMetadata(string.Empty)
        );

    public static readonly DependencyProperty ValProperty = DependencyProperty.Register
            (
                 "Val",
                 typeof(string),
                 typeof(AttributeStack),
                 new PropertyMetadata(string.Empty)
            );

    public static readonly DependencyProperty ValTextProperty = DependencyProperty.Register
        (
                "ValText",
                typeof(string),
                typeof(AttributeStack),
                new PropertyMetadata(string.Empty)
        );

    public string Val
    {
        get { return (string)GetValue(ValProperty); }
        set { SetValue(ValProperty, value); }
    }

    public string ValText
    {
        get { return (string)GetValue(ValTextProperty); }
        set { SetValue(ValTextProperty, value); }
    }


    public string Attr
    {
        get { return (string)GetValue(AttrProperty); }
        set { SetValue(AttrProperty, value); }
    }


    public AttributeStack()
    {
        InitializeComponent();

        Binding valBinding = new Binding("Val")
        {
            Source = Val,
            Mode = BindingMode.OneWay
        };

        Binding textBinding = new Binding("Content")
        {
            Source = this.ValLabel.Content,
            Mode = BindingMode.OneWay
        };

        ValLabel.SetBinding(Label.ContentProperty, valBinding);
        this.SetBinding(AttributeStack.ValTextProperty, textBinding);
    }
}

Page XAML

<Page x:Class="Pipeline_General.SceneView" x:Name="page"
  xmlns:pm="clr-namespace:Pipeline_General"
  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" 
  xmlns:cc="clr-namespace:Pipeline_General.Custom_Controls"
  d:DesignHeight="800" d:DesignWidth="600"
  DataContext = "{Binding RelativeSource={RelativeSource Self}}"
  Title="SceneView">
<Grid>
    <ScrollViewer>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <StackPanel Grid.Column="0">
                <Expander IsExpanded="True" Margin="5">
                    <Expander.Header>
                        <Grid x:Name="Grid" HorizontalAlignment="Stretch" Width="NaN">
                            <Border x:Name="Border" Background="Transparent" HorizontalAlignment="Stretch" BorderBrush="{x:Static pm:myBrushes.blue}" BorderThickness="0,0,0,1"
                        Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Expander}}, Path=ActualWidth}">
                                <TextBlock x:Name="HeaderText" FontSize ="18" FontFamily="Calibri" Foreground="{x:Static pm:myBrushes.blue}" Text="General Info: " />
                            </Border>
                        </Grid>
                    </Expander.Header>
                    <StackPanel>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Title:" Val="{Binding Path=DataContext.Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Episode:" Val="{Binding Path=DataContext.episode.Key, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <cc:AttributeStack Attr="Sequence:" Val="{Binding Path=DataContext.sequence.Key, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <cc:AttributeStack Attr="Scene:" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Assigned:" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <cc:AttributeStack Attr="Status:" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Last Saved (Time):" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <cc:AttributeStack Attr="Last Saved (User):" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Due:" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                    </StackPanel>
                </Expander>
            </StackPanel>

            <StackPanel Grid.Column="1">
                <cc:Playblast_Viewer  x:Name="PlayblastView"/>
                <cc:FeedbackCtrl Header="Feedback:"/>
                <cc:FeedbackCtrl Header="NoticeBoard:"/>
            </StackPanel>

        </Grid>
    </ScrollViewer>
</Grid>

The label binding to the DataContext.Title field appears to be working, and setting the attr fields appears to be working correctly/as expected also. Therefore it is just the Val fields that I cannot get working..

Updated to include the page code behind. there is almost nothing going on here though.

public partial class SceneView : Page
{
    public Scene scene { get; set; }
    public SceneView(Scene s)
    {
        InitializeComponent();
        scene = s;
        this.DataContext = scene;
    }
}

I understand that im pretty newb in DP's so there is a high possibility that what im trying to do could be done better a completely different way. In this case, the scene object has a multitude of fields that Im using the page and user controls to expose. Ive written the entire app in code behind without any xaml, but now i am trying to teach myself xaml and have the next version of the prototype be much more fluent and reduce the required coding by 80%.

The scene object, which is the data context of the page object, I attempting to send fields from to the user controls ie. page.DataContext.episode.Key is a legitimate field in code behind. And through the above xaml i am looking for its value to be sent to the Val field, which would then update the ValText ( a label in the usercontrol ) Content property.

Ive figured out the issue / issues.. Most of them because im still a newb at xaml.

I realised that the datacontext does not access fields if they are not declared with get/set accessors. I changed the scene declaration in page code behind so that it was a dependency object Instead of chaining bindings I used a dependency property changed event to set the usercontrols label content.

public partial class AttributeStack : UserControl
{
    #region Val (DependencyProperty)
    public string Val
    {
        get { return (string)GetValue(ValProperty); }
        set { SetValue(ValProperty, value); }
    }
    public static readonly DependencyProperty ValProperty =
        DependencyProperty.Register("Val", typeof(string), typeof(AttributeStack),
          new PropertyMetadata { PropertyChangedCallback = ValChanged });
    private static void ValChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        AttributeStack aStack = d as AttributeStack;
        if (aStack != null && e.NewValue != null)
        {
            aStack.ValLabel.Content = e.NewValue.ToString();
        }
    }
    #endregion

    public static readonly DependencyProperty AttrProperty = DependencyProperty.Register
        (
             "Attr",
             typeof(string),
             typeof(AttributeStack),
             new PropertyMetadata(string.Empty)
        );

    public string Attr
    {
        get { return (string)GetValue(AttrProperty); }
        set { SetValue(AttrProperty, value); }
    }

    public AttributeStack()
    {
        InitializeComponent();
    }
}

}

public partial class SceneView : Page
{
    public static readonly DependencyProperty sceneProperty = DependencyProperty.Register
        (
             "scene",
             typeof(Scene),
             typeof(SceneView)
        );

    public Scene scene
    {
        get { return (Scene)GetValue(sceneProperty); }
        set { SetValue(sceneProperty, value); }
    }
    public SceneView(Scene s)
    {
        InitializeComponent();
        scene = s;

        this.DataContext = scene;
    }
}

<Page x:Class="Pipeline_General.SceneView" x:Name="page"
  xmlns:pm="clr-namespace:Pipeline_General"
  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" 
  xmlns:cc="clr-namespace:Pipeline_General.Custom_Controls"
  d:DesignHeight="800" d:DesignWidth="600"
  DataContext = "{Binding RelativeSource={RelativeSource Self}}"
  Title="SceneView">
<Grid>
    <ScrollViewer>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <StackPanel Grid.Column="0">
                <Expander IsExpanded="True" Margin="5">
                    <Expander.Header>
                        <Grid x:Name="Grid" HorizontalAlignment="Stretch" Width="NaN">
                            <Border x:Name="Border" Background="Transparent" HorizontalAlignment="Stretch" BorderBrush="{x:Static pm:myBrushes.blue}" BorderThickness="0,0,0,1"
                        Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Expander}}, Path=ActualWidth}">
                                <TextBlock x:Name="HeaderText" FontSize ="18" FontFamily="Calibri" Foreground="{x:Static pm:myBrushes.blue}" Text="General Info: " />
                            </Border>
                        </Grid>
                    </Expander.Header>
                    <StackPanel>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Title:" Val="{Binding Path=scene.Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Episode:" Val="{Binding Path=scene.episode.Key, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <cc:AttributeStack Attr="Sequence:" Val="{Binding Path=scene.sequence.Key, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <cc:AttributeStack Attr="Scene:" Val="{Binding Path=scene.Key, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Assigned:" Val="{}"/>

                        <cc:AttributeStack Attr="Status:" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Last Saved (Time):" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <cc:AttributeStack Attr="Last Saved (User):" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                        <Separator Foreground="Transparent" Height="0" Margin="5"/>
                        <cc:AttributeStack Attr="Due:" Val="{Binding Path=Title, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Page}}"/>
                    </StackPanel>
                </Expander>
            </StackPanel>

            <StackPanel Grid.Column="1">
                <cc:Playblast_Viewer  x:Name="PlayblastView"/>
                <cc:FeedbackCtrl Header="Feedback"/>
                <cc:FeedbackCtrl Header="NoticeBoard"/>
            </StackPanel>

        </Grid>
    </ScrollViewer>
</Grid>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM