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!
ChildControl
I gave the UserControl a Name in Xaml i.e
<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.
ParentControl
I did the same as with ChildControl. i.e. 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(c.R, c.G, c.B);
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.
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:
Make sure the parent's childForeColor has a color assigned to it especially when using ordinary properties.
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)
perhaps use FrameworkPropertyMetadata instead and then add flag AffectsRender - don't think this is the problem, but worth a shot
Play around with the Binding Mode - although I do not think this is the real issue either
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.
Related
I have a user control and im trying to bind one of its properties
User Control Xaml
<UserControl x:Class="pi_browser.Testing.Example"
...
x: Name="LabelControl">
<StackPanel x:Name="RootStackPanel">
<Label Content="{Binding Text, ElementName=LabelControl}"/>
</StackPanel>
</UserControl>
User Control Codebehind
public partial class Example : UserControl
{
public Example()
{
InitializeComponent();
ExampleViewModel vm = new ExampleViewModel(State);
DataContext = vm;
}
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State",
typeof(Boolean), typeof(Example), new PropertyMetadata(false));
}
Main Page View Model
class ExampleViewModel
{
public ExampleViewModel(bool v)
{
val = v;
}
bool val;
public string Text { get => val ? "This worked" : "This didnt work"; }
}
Main Window Xaml
<Window x:Class="pi_browser.Testing.Tester" ... >
<Grid>
<local:Example State="True"/>
</Grid>
</Window>
In this example I didn't bind the State variable, I only passed a literal, but ideally I would like to bind to actual values.
State is a boolean, yet you bind to Text. Let us fix one issue by creating a dependency property Text on your User Control. We shall fix the Text issue and not the boolean State issue. Once you fix that, do the same for State.
So to fix Text we need to fix why this fails:
<Label Content="{Binding Text, ElementName=LabelControl}"/>
You set the ElementName to be the UserControl itself, which is what one wants. But then you tell the binding to look for (remember binding is just reflection of an object under the covers) the property Text. The property Text does not exist on that instance/class...but State does. Its obvious to bind to a newly created Text dependency property on the user control to fix the first issue.
Then when you instantiate the control on your main page, you need to then, and only then bind to Text because that property also resides on your viewmodel.
So three things, along with the change mentioned on the UserControl:
Make your ViewModel adhere to INotifyPropertyChanged and make the Text property use the notification mechanism you install.
Make sure that your main page has its DataContext set to a vailid instance of your ViewModel class.
Bind to Text such as <local:Example State="{Binding Text}"/>
Once that is done, the Text value will properly flow towards the UserControl.
I want to be able to hide a progress bar once a button is pressed, but it doesn't seem to be working.
This is my progress bar in XAML :
<ProgressBar Grid.Row="1" Grid.Column="1" IsIndeterminate="True" Height="37" Margin="0,10,0,10" Visibility="{Binding ProgressVisibility, Mode=TwoWay}" RenderTransformOrigin="0.5,0.5"/>
In my .xaml.cs I have this dependency property :
public Visibility ProgressVisibility
{
get { return (Visibility)GetValue(ProgressVisibilityProperty); }
set { this.SetValue(ProgressVisibilityProperty, value); }
}
public static readonly DependencyProperty ProgressVisibilityProperty =
DependencyProperty.Register("ProgressVisibility", typeof(Visibility), typeof(RecipeDownloadDialogStatusView), new PropertyMetadata(Visibility.Visible));
And in the view model I have this property :
private Visibility progressVisibility;
public Visibility ProgressVisibility
{
get
{
return this.progressVisibility;
}
set
{
this.progressVisibility = value;
this.OnPropertyChanged("ProgressVisibility");
}
}
When a button is pressed, I call :
ProgressVisibility = Visibility.Hidden;
While debugging :
The line above gets executed but nothing happens.
The setter gets called and so is the OnPropertyChanged
The dependency property gets registred.
I'm not really sure what did I do wrong. Any help will be appreciated.
Based on your question I think one of two things is the problem here:
The DataContext of the ProgressBar is set to your viewmodel but your Button is only updating the property of your parent control / window.
The DataContext of the ProgressBaris set to your parent control / window but your Button is only updating the property of your viewmodel.
If you want ProgressVisibility to be publicly available, change it to
public Visibility ProgressVisibility
{
get => this.viewModel.ProgressVisibility;
set => this.viewModel.ProgressVisibility = value;
}
If ProgressVisibility does not need to be public, just drop the dependency property and work with the implementation in the viewmodel instead.
Why does TextBlock "T1" not show "101" after clicking on Button "B1" and still shows "100"?
<StackPanel>
<TextBlock Name="T1" Text="{x:Bind value, Mode=OneWay}"/>
<Button Name="B1" Content="+1" Click="B1_Click"/>
</StackPanel>
and
public sealed partial class MainPage : Page
{
public int value;
public MainPage()
{
InitializeComponent();
value = 100; // initial value
}
private void B1_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
value = value + 1;
}
}
Your value is a field, yes it works with x:Bind. Actually it should even work being private.
But in order for the UI to update the value of value, you need to make one of the three changes below -
Call Bindings.Update() after setting it.
change it to a dependency property.
Change it to a normal property but implement INotifyPropertyChanged on your page and raise property changed event on the setter. You can read more from here.
But which one to pick? This is normally what I do -
If the property rarely changes, I use Bindings.Update() and remove Mode=OneWay from the binding to have the best performance.
If the property lives in the code-behind of a UI element (like in your case), I go with a dependency property.
If the property lives inside a ViewModel, I use INPC.
In my user control (class LabeledBox), I've added dependency property as follows.
public static readonly DependencyProperty HorizontalProperty
= DependencyProperty.Register(
"Horizontal",
typeof (Orientation),
typeof (LabeledBox),
new PropertyMetadata(default(Orientation)));
public Orientation Horizontal
{
get { return (Orientation) GetValue(HorizontalProperty); }
set { SetValue(HorizontalProperty, value); }
}
However, when setting it according to the below, doesn't give me any difference in behavior. In fact, the setter in the property doesn't get called. What do I miss?
<local:LabeledBox x:Name="Info field"
Description="Info"
Horizontal="Horizontal" />
The component in question has a stack panel as outermost control and it's bound like this.
<StackPanel Name="TheContainer" Orientation="{Binding Horizontal}">
Perhaps I've done the binding incorrectly?
Give a name to your UserControl:
<UserControl .... x:Name="labeledBox">
And use the binding like this:
<StackPanel Name="TheContainer" Orientation="{Binding Horizontal, ElementName=labeledBox}">
Yes your Binding is not well try to update it to be seems like:
<StackPanel Name="TheContainer"
Orientation="{
Binding Horizontal,
RelativeSource={RelativeSource AncestorType=local:LabeledBox}}"/>
I have a UserControl with one DependencyProperty which sets in codebehind (I guess this may be a source of my problem, but still don't know what to do):
UserControl
public partial class MyControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyControl),
new FrameworkPropertyMetadata("",FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); InvokePropertyChanged(new PropertyChangedEventArgs("Text"));}
}
public static string GetText(DependencyObject obj)
{
return (string)obj.GetValue(TextProperty);
}
public static void SetText(DependencyObject obj, string value)
{
obj.SetValue(TextProperty, value);
}
private void ChangeText()
{
Text="some value";
}
}
In my View.xaml I use this control like this:
<MyControl Text="{Binding Text, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
And the Text property in my ViewModel:
private string _text;
public string Text
{
get { return _text; }
set { _text= value; InvokePropertyChanged(new PropertyChangedEventArgs("Text"));}
}
The problem:
Text property in the ViewModel never gets updated; when use binding with a regular control like TextBox, all works perfect; if I set Text in XAML, Text propery of UserControl updates.
What I did wrong?
UPDATE
My issue was that I have set DataContext explicitly on MyControl.
Issue is in your Binding:
Text="{Binding Text, RelativeSource={RelativeSource Self},
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Text property is in your ViewModel but you are referring to itself by using RealtiveSource to point back to self. So, it's binding Text DP with itself.
If you have set DataContext of your control, it will automatically inherit DataContext from parent. So, you don't need RelativeSource at all.
It simply should be:
Text="{Binding Text}"
Few points more (but not related to your issue):
Since you target to use this property from within control, so go for normal DP instead of attached property.
Since at time of registration, you have set it to bind TwoWay by default. No need to explicitly do that at time of binding.
Remove InvokePropertyChanged call from your DP wrapper setter. Setter won't be called from XAML and also DP is already PropertyChanged aware.
UPDATE
In case DataContext of MyControl is set to instance of another class, above approach will search for Text property in MyControl DataContext.
You can pass DataContext of parent control (StackPanel in your case) like this:
Text="{Binding DataContext.Text, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=StackPanel}}"
You have registered your property as attached, yet you are also using it as a regular DependencyProperty. I think that the xaml parser gets confused. Decide which one you want to use.