简体   繁体   中英

WPF Dependency property binding breaks and always uses the default value

I have a dependency property defined as below. It is defined in xaml.cs of Childusercontrol. It always uses the default value of RGB(255,0,0) ie. Red .

public Color ForeColor
{
    get {return (Color)this.GetValue(ForeColorProperty); }
    set {this.SetValue(ForeColorProperty, value);}
}

public static readonly DependencyProperty ForeColorProperty = DependencyProperty.Register("ForeColor", typeof(Color), typeof(Childusercontrol), new PropertyMetadata(Color.FromRgb(255,0,0), OnCurrentForeColorPropertyChanged));

private static void OnCurrentForeColorPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{    
    Childusecontrol control = source as Childusecontrol;
    Color fcolor= (Color)e.NewValue;
}

The value is passed through xaml from parent usercontrol as

<UC:Childusercontrol ForeColor="{Binding ChildForeColor}"/>

ChildForeColor is a property of type Color in ViewModel of ParentUserControl and is defined as below.

 private Color _ChildForeColor;
    public Color ChildForeColor 
    {
    get
    {
    return _ChildForeColor ;
    }
    set
    {
    if (_ChildForeColor  != value)
    {
    _ChildForeColor  = value;
    RaisePropertyChanged(()=> ChildForeColor );
    }
    }
    }

And ChildForeColor property is set as below, in the parentusercontrol's constructor.

The value being passed as constructor parameter is blue .

public Parentusercontrol(System.Drawing.Color ForeColor)
{
ChildForeColor  = Color.FromRgb(ForeColor.R, ForeColor.B, ForeColor.G);
}

But, the InitializeComponent(); of Parent control's xaml.cs clears the value of dependency property and hence, only the default value is used.

Do I have to change the definition of the dependency property? How to fix this bug?

This worked perfectly fine for me!

  1. ChildControl

    I gave the UserControl a Name in Xaml ie

     <UserControl ... (all normal namespaces)... x:Name="Child"> <Border> <Border.Background> <SolidColorBrush Color="{Binding ForeColor, ElementName=child}"/> </Border.Background> </Border> </UserControl> 

    The property "ForeColor" is a dependency property as you defined it yourself. This control works perfectly on its own too.

  2. ParentControl

    I did the same as with ChildControl. ie gave it a name.

     <UserControl ... (Usual NS)... x:Name="parent"> <Border BorderThickness="2"> <local:ChildUserControl Margin="5" ForeColor="{Binding ChildForeColor, ElementName=parent}"/> <Border.BorderBrush> <SolidColorBrush Color="{Binding ChildForeColor, ElementName=parent}"/> </Border.BorderBrush> </Border> </UserControl> 

    This also works fine with testing the C# Class looks as follows

     public ParentUserControl(System.Drawing.Color c) { InitializeComponent(); Color c2 = Color.FromRgb(cR, cG, cB); ChildForeColor = c2; } private Color _ChildForeColor = Color.FromRgb(0, 255, 0); public Color ChildForeColor { get { return _ChildForeColor; } set { if (value != _ChildForeColor) { _ChildForeColor = value; OnPropertyChanged(() => ChildForeColor); } } } 

    I have assigned the _ChildForeColor a value just for testing, but this is not needed. Please note however that if you run a NotifyPropertyChanged event this cannot happen before InitializeComponent(); This I guess is because nothing yet has been initialized to listen to the change. Therefore you have 2 options. Remove OnPropertyChanged and assign color before InitializeComponent, or use OnPropertyChanged but only assign color after InitializeComponent. The first solution will still work because the property value is changed before the components go and look for the value.

  3. Window for using constructing the controls

    This is a bit more tricky as you have assigned a constructor that takes a variable. So my code looks as follows:

     public Control ParContent { get { return (ContentControl)GetValue(ParContentProperty); } set { SetValue(ParContentProperty, value); } } //Register Dependency ParContent Property public static readonly DependencyProperty ParContentProperty = DependencyProperty.Register("ParContent", typeof(ContentControl), typeof(MainWindow), new PropertyMetadata( )); public MainWindow() { InitializeComponent(); ParContent = new ParentUserControl(System.Drawing.Color.Blue); } 

    and in Xaml

     <Window ...Title="MainWindow" Height="478.784" Width="736.87" x:Name="win"> <Grid> <local:ChildUserControl HorizontalAlignment="Left" Height="100" Margin="122,298,0,0" VerticalAlignment="Top" Width="100"/> <ContentControl x:Name="Parent" Content="{Binding ParContent,ElementName=win}" HorizontalAlignment="Left" Margin="106,49,0,0" VerticalAlignment="Top" Height="79" Width="93"/> </Grid> </Window> 

As I said this worked perfectly fine by me and all the properties keep their values.

Possible solutions:

  1. Make sure the parent's childForeColor has a color assigned to it especially when using ordinary properties.

  2. If you use ordinary properties in Parent control make sure INotifyPropertyChange is called if the color is changed after Initialize (Which I guess you subscribe to already)

  3. perhaps use FrameworkPropertyMetadata instead and then add flag AffectsRender - don't think this is the problem, but worth a shot

  4. Play around with the Binding Mode - although I do not think this is the real issue either

  5. If you are working with 2 x controls where 1 property is most likely going to inherit from another use Inherited properties rather - http://msdn.microsoft.com/en-us/library/ms753197(v=vs.110).aspx

Bottom line I have a suspicion that the Parent's "ChildForeColor" might be causing the problem as the above seems ok to me at first glance.

EDIT

Try doing the following. In xaml give your parent control a name x:name="Parent" then in the binding mode do this

<UC:Childusercontrol ForeColor="{Binding ChildForeColor, ElementName="Parent"}"/>

This should sort out any binding issues if the problem lies with the binding.

However you say "Parent control's xaml.cs clears the value of dependency property and hence, only the default value is used." Which indicates that the problem is not with binding or with the child control as far as I can gather...

I also assumed you have stepped through the code so after you hit this

ChildForeColor  = Color.FromRgb(ForeColor.R, ForeColor.B, ForeColor.G);

ChildForeColor appears correct and then if you override OnInitialized() and evaluate the value of ChildForeColor after base.OnInitialized(e); has run the ForeColor is still unchanged?

With this I also assume you have not removed InitializeComponent(); from the constructor, and InitializeComponent(); comes after ChildForeColor = ....! In your constructor you do not show where InitializeComponent() is and I assumed it was just for easy reading purpose.

If ForeColor remained unchanged at this point and assuming base.OnInitialized is the first method that runs in OnInitialized. Then Initialization is not the problem, then the alternative suggestion is to change ChildForeColor to a proper dependency property:

public Color ChildForeColor
    {
        get { return (Color)GetValue(ChildForeColorProperty); }
        set { SetValue(ChildForeColorProperty, value); }
    }

    //Register Dependency ChildForeColor Property
    public static readonly DependencyProperty ChildForeColorProperty = DependencyProperty.Register("ChildForeColor", typeof(Color), typeof(ParentControl), new FrameworkPropertyMetadata());

and see if that changes it.

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