Dependecy Property works as source but not as target [duplicate] - wpf

This question already has answers here:
What's wrong with "DataContext = this" in WPF user controls?
(1 answer)
Data binding to a UserControl in WPF
(4 answers)
Closed 2 years ago.
I have a dependency property in a user control.
public ComputerListView()
{
InitializeComponent();
vm = new ComputerViewModel();
this.DataContext = vm;
DispatcherTimer time = new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Normal,
delegate
{
SetValue(AutoCounterProperty, vm.SimpleCounter);
},
Dispatcher);
}
public int AutoCounter
{
get { return (int)GetValue(AutoCounterProperty); }
set { SetValue(AutoCounterProperty, value); }
}
// Using a DependencyProperty as the backing store for Counter. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AutoCounterProperty =
DependencyProperty.Register("AutoCounter", typeof(int), typeof(ComputerListView), new PropertyMetadata(0));
}
<StackPanel>
<local:ComputerListView x:Name="computerListView" AutoCounter="{Binding VMCounter, Mode=TwoWay}"/>
<TextBox Background="Beige" Text="{Binding VMCounter, Mode=TwoWay}"/>
<TextBlock Background="LightCyan" Text="{Binding ElementName=computerListView, Path=AutoCounter}"/>
<Button Content="VMCounter" Click="VMCounter_Button_Click"/>
</StackPanel>
I expect that the VMCounter property will get updated as the AutoCounter will run in dependency property and the TextBox will be updated from the VMCounter property but it shows 0, never gets updated.
I have use the AutoCounter the way I have in TextBlock, then it updates fine.
I thought the major use of DP is to be able to used as target property like in first case. Why it doesn't work that way?

Related

DependencyProperty refreshing only one time

I have a custom control with DependencyProperty
public Point EndPoint
{
get { return (Point)GetValue(EndPointProperty); }
set { SetValue(EndPointProperty, value); }
}
public static readonly DependencyProperty EndPointProperty =
DependencyProperty.Register("EndPoint", typeof(Point), typeof(Speedometer), new FrameworkPropertyMetadata(new Point(100,100),FrameworkPropertyMetadataOptions.AffectsRender));
and somewhere in DefiningGeometry logic I'm calling
EndPoint = new Point(xEnd, yEnd);
In xaml, where I use this, I have another control and I'm trying to bind Canvas.Left to EndPoint.X of my custom control
<Rectangle Fill="White" Height="50" Width="50" x:Name="Grip" Canvas.Left="{Binding ElementName=control, Path=EndPoint.X}" Canvas.Top="{Binding ElementName=control, Path=EndPoint.Y}">
Update seems to be fired only once, because result depends of DependencyProperty default value.
How to make it updating all time when value of property is changed?
Ok, I got it.
First, I implemented INotifyPropertyChanged in my custom control, then after set Property, I'm calling OnPropertyChanged(nameof(EndPoint)); I thought DependencyProperty can notify property changes two way.

WPF MVVM: Strange Binding behavior

I have a UserControl that contains a TabControl.
<UserControl x:Class="Test.MyUC"
....
xmlns:vm="clr-namespace:Test.ViewModels"
xmlns:ikriv="clr-namespace:IKriv.Windows.Controls.Behaviors"
...
<UserControl.Resources>
<vm:MyUCVM x:Key="VM" />
</UserControl.Resources>
<UserControl.DataContext>
<StaticResourceExtension ResourceKey="VM" />
</UserControl.DataContext>
<!-- Using Ivan Krivyakov's Attached Behavior -->
<TabControl ikriv:TabContent.IsCached="True"
TabStripPlacement="Top" ItemsSource="{Binding TabList}" IsSynchronizedWithCurrentItem="True">
<TabControl.Resources>
<DataTemplate DataType="{x:Type vm:MyTab1VM}">
<v:MyTab1/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MyTab2VM}">
<v:MyTab2/>
</DataTemplate>
</TabControl.Resources>
...
Of course, in MyUCVM, I have TabList. Now, up to this point, everything works fine.
The problem starts when one of the tabs (e.g. MyTab1) in the TabControl needs to continuously and recursively read data from some external source (done in the ViewModel of course), and pass that data to View (via Binding) to display. Even up to this point everything is working. However, I do not want that to run when the tab is not visible, because there is no point to do that.
To do that, MyTab1VM needs to know if the associated View (MyTab1) is the selected tab. Therefore, I wired this up:
MyTab1:
<Style TargetType="TabItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=OneWayToSource}" />
</Style>
MyTab1VM
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected",
typeof(bool),
typeof(MyTab1VM),
new PropertyMetadata(false, new PropertyChangedCallback(IsSelectedChanged))
);
public bool IsSelected
{
get
{
return (bool) GetValue(IsSelectedProperty);
}
set
{
SetValue(IsSelectedProperty, value);
}
}
public static void IsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.Property == IsSelectedProperty)
{
MyTab1VM vm = d as MyTab1VM ;
vm.SetupToGetData();
}
}
private void SetupToGetData()
{
if (this.IsSelected)
{
System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(100);
timer.Tick += timer_Tick;
timer.Start();
}
}
private void timer_Tick(object sender, EventArgs e)
{
if (this.IsSelected)
this.MyData = ExternalSource.GetData();
else
{
(sender as System.Windows.Threading.DispatcherTimer).Stop();
}
}
Unfortunately, this setup only works when I set this.IsSelected = true; manually in the MyTab1VM's constructor. Leaving that out in the constructor, the data do not get shown in the view.
I have set breakpoints and confirmed that the binding for IsSelected is running correctly. Even the timer is running, and ExternalSource.GetData() is being called. But this.MyData = ExternalSource.GetData(); is not triggering the change from the ViewModel to the View.
The most puzzling part is that the same binding is triggered if IsSelected is set to true from the constructor.
Anyone out there knows what happened here?
I managed to do some fruitful troubleshooting on my own. I made a breakpoint in SetupToGetData() and I put this.GetHashCode() in my debugging watchlist. When I manually set this.IsSelected = true in the constructor, I realized that the SetupToGetData() method is called twice, with two different hash values. Planting another breakpoint in the constructor also showed that the constructor is called when I switch to this tab.
I have decided to move this to a new question, because it looks highly possible that the problem has nothing to do with binding.
Edit
Seems like I was right that this is the root of this problem. As that question is solved, so is this as well.

UserControl enum DependencyProperty not binding

I created a UserControl that contains 3 DependencyProperties. Two are working fine, but there is one that gives me a real headache.
I have an enum (outside the UserControl class but same namespace):
public enum RecordingType
{
NoRecording,
ContinuesRecording,
EventRecording
}
I created a DependencyProperty for it as follows:
public static DependencyProperty SelectedRecordingTypeProperty = DependencyProperty.Register("SelectedRecordingType", typeof(RecordingType), typeof(SchedulerControl),
new FrameworkPropertyMetadata((RecordingType)RecordingType.NoRecording, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public RecordingType SelectedRecordingType
{
get
{
return (RecordingType)GetValue(SelectedRecordingTypeProperty);
}
set
{
SetValue(SelectedRecordingTypeProperty, value);
}
}
and I'm using it in XAML like this:
<userControls:SchedulerControl
Grid.Row="1"
Grid.Column="3"
SelectedRecordingType="{Binding CurrentRecordingType,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
FullRecordingSchedule="{Binding MondayFullRecordingSchedule,UpdateSourceTrigger=PropertyChanged}"
SelectedRecordingTime="{Binding MondaySelectedRecordingTime,UpdateSourceTrigger=PropertyChanged}"/>
There are two more DependencyProperties that work just fine (I get to their get and set methods inside the UserControl), but this one is just a no-go. I created DPs before and I'm doing everything the same. I also made sure the binding in my VM is ok and the getter and setter are being called correctly.
Any help would be great!
Also I checked that I my VM. The binding does execute.
Let me show an other solution for UserControl (UC from now) with a ComboBox and Enum bindings.
Also a common problem, when you can bind the enum, but you can't get the SelectedItem of the ComboBox from the UC. This solution will also provide the SelectedItem.
For example, I have an ExampleUC : UserControl UC class, which is able to accept an enum, and to provide the SelectedItem. It will do it using properties (attributes in .xaml).
I also have an enum, called ExampleEnum, and the Window, which creates a new instance of ExampleUC and setting that's properties/attributes.
In the ExampleUC.xaml:
<UserControl x:Class="TestNamespace.View.ExampleUC"
xmlns:view="clr-namespace:TestNamespace.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:markup="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType={markup:Type view:ExampleUC}}}">
<ComboBox ItemsSource="{Binding EnumTypeArray}" SelectedItem="{Binding SelectedItem}"/>
</Grid>
</UserControl>
As you can see, the DataContext of the UC has been set to it's ancestor's DataContext, which means it can receive the wanted parameters (you can find more explanations about DataContext inheritance and visual tree, just make some researches about them).
The binded properties (EnumTypeArray and SelectedItem) are DependencyProperties in the ExampleUC.xaml.cs file:
public Array EnumTypeArray
{
get { return (Array)GetValue(EnumTypeArrayProperty); }
set { SetValue(EnumTypeArrayProperty, value); }
}
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty EnumTypeArrayProperty =
DependencyProperty.Register("EnumTypeArray", typeof(Array), typeof(ExampleUC), new PropertyMetadata(new string[0]));
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(ExampleUC), new PropertyMetadata(null));
To create new DependencyProperty you can use the propdp code snippet. (Write it and press TAB by default). These properties will be shown as attributes in the .xaml editor, when you create and edit the instances of ExampleUC.
At this stage you have a UC, which can accept an enum, and return the SelectedItem.
The enum somewhere:
public enum ExampleEnum
{
Example1,
Example2
}
The Window, which uses the ExampleUC:
You have to add a new resource to the Window's resources, which will be an ObjectDataProvider in order to be able to use your enum as ItemsSource:
<Window.Resources>
<ObjectDataProvider x:Key="MyEnumName" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:ExampleEnum"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Please note that, the local namespace prefix has been defined earlier at the namespaces' section, which is the namespace of ExampleEnum, for example:
xmlns:local="clr-namespace:TestNamespace.Data"
To use ExampleUC, in a Grid or Panel, use the following:
<views:ExampleUC EnumTypeArray="{Binding Source={StaticResource MyEnumName}}" SelectedItem="{Binding MyProperty, Mode=TwoWay}"/>
To set the Mode to TwoWay is necessary to be able to get and set the property.
Please note that, you might have to define the views namespace at the namespaces' section, if Visual Studio wouldn't do it for you.
As you can see, the previously defined DependencyProperties are showing up as attributes. The EnumTypeArray is responsible to fill the ComboBox's items, and the SelectedItem has been binded to MyProperty, which is a property in the model class, such as:
public ExampleEnum MyProperty{
get{ return _myProperty;}
set{
_myProperty = value;
OnPropertyChanged("MyProperty");
}
}
This example only shows how to use enums through UCs. Since this UC has only a single component (a ComboBox), it's useless in practice. If you decorate it with a Label or others, it would do the job.
Hope it helps.

WPF custom control databinding

I'm new to the development of custom controls in WPF, but I tried to develop a single one to use in a application that I'm developing. This control is an autocomplete textbox. In this control, I have a DependencyProprety that has a list of possible entries so a person can choose from while entering the text
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource",typeof (IList<object>),typeof (AutoCompleteTextBox),new PropertyMetadata(null));
public IList<object> ItemsSource
{
get { return (IList<object>) GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
RaiseOnPropertyChanged("ItemsSource");
}
}
I use this control in a usercontrol and associate this control to a property in the viewmodel
<CustomControls:AutoCompleteTextBox Height="23" Width="200"
VerticalAlignment="Center" Text="{Binding Path=ArticleName, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Path=Articles,
Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
</CustomControls:AutoCompleteTextBox>
I have a viewmodel that I assign on the usercontrol load to the datacontext of the usercontrol load
protected virtual void Window_Loaded(object sender, RoutedEventArgs e)
{
if (!DesignerProperties.GetIsInDesignMode(this))
{
this.DataContext = viewModel;
SetLabels();
}
}
This viewmodel has the property Articles with values but the ItemsSource property of the control is null when I try to search in the list after the user enter some text.
Is there any special step that I missed when I create the control so use the mvvm pattern.
I hope that the explain the problem in a understandable way. Any help/hints would be welcome.
There are two issues here:
First, you're dependency property is defining the "default" value for this property to be null. You can change that by changing the metadata to specify a new collection:
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource",typeof (IList<object>),typeof (AutoCompleteTextBox),
new PropertyMetadata(new List<object>));
Secondly, when using dependency properties, the setter can't contain any logic. You should keep your property set as:
public IList<object> ItemsSource
{
get { return (IList<object>) GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
This is because the setter doesn't actually get called by the binding system - only when you use code. However, since the class is a DependencyObject and this is a DP, you don't need to raise property changed events.

Setting XAML property value to user control

I have a user control in WPF which i want the text of one of it's labels to be read from the XAML where it is used. Hence..
My User Control:
<UserControl x:Class="muc">
<Label Foreground="#FF7800" FontSize="20" FontWeight="Bold">
<Label.Content>
<Binding ElementName="TestName" Path="." />
</Label.Content>
</Label>
</UserControl>
Then using it:
<mycontorls:muc TestName="This is a test" />
But it doesn't works ...
How can i read the properties ?
I tried the first two answers and what I got worked in code but not on XAML (also doesn't let you see changes in the design view when using the control).
To get a property working like any other native one, here is the full process:
(The sample adds a dependency property of type Nullable to show in the control as text or a default if null)
In the code file:
1.a Define a dependency property:
public static readonly DependencyProperty MyNumberProperty = DependencyProperty.Register("MyNumber", typeof(Nullable<int>), typeof(MyUserControl), new PropertyMetadata(null, new PropertyChangedCallback(OnMyNumberChanged)));
1.b Implement the OnMyNumberChanged Callback:
private static void OnMyNumberChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args){
// When the color changes, set the icon color PlayButton
MyUserControl muc = (MyUserControl)obj;
Nullable<int> value = (Nullable<int>)args.NewValue;
if (value != null)
{
muc.MyNumberTextBlock.Text = value.ToString();
}
else
{
muc.MyNumberTextBlock.Text = "N/A";
}
}
1.c implement the MyNumber property (not dependency) to use the dependency property for easy in code use:
public Nullable<int> MyNumber{
get
{
return (Nullable<int>)GetValue(MyNumberProperty);
}
set
{
SetValue(MyNumberProperty, value);
OnTargetPowerChanged(this, new DependencyPropertyChangedEventArgs(TargetPowerProperty, value, value)); // Old value irrelevant.
}
}
In the XAML file bind the TextBlock control's text to the property (not dependency) to get the default value of the dependency property in case it is not set by the user of the control (assuming you called your root element of the user control "RootElement"):
This code:
< TextBlock Name="MyNumberTextBlock" Text="{Binding MyNumber, ElementName=RootElement}"/>
If you give the root UserControl element a name, then you can refer to it using ElementName:
<UserControl x:Class="muc"
Name="rootElement">
<Label Foreground="#FF7800" FontSize="20" FontWeight="Bold">
<Label.Content>
<Binding ElementName="rootElement" Path="TestName" />
</Label.Content>
</Label>
</UserControl>
You can also use the markup extension syntax to make it a little shorter:
<UserControl x:Class="muc"
Name="rootElement">
<Label Foreground="#FF7800" FontSize="20" FontWeight="Bold"
Content="{Binding TestName, ElementName=rootElement}"/>
</UserControl>
Also remember that your control will be created before its properties are set. You will either need to implement INotifyPropertyChanged or have TestName be a dependency property so that the binding is re-evaluated after the property is set.
I've only done this with Silverlight, but i wouldnt be surprised if it works in the exact same way!
// <summary>
// Xaml exposed TextExposedInXaml property.
// </summary>
public static readonly DependencyProperty TestNameProperty = DependencyProperty.Register("TestName", typeof(string), typeof(NameOfMyUserControl), new PropertyMetadata(string.empty));
// <summary>
// Gets or sets the control's text
// </summary>
public string TextExposedInXaml
{
get
{
return (string)GetValue(TestNameProperty );
}
set
{
SetValue(TestNameProperty , value);
// set the value of the control's text here...!
}
}
{Binding ElementName=x} binds to an element with name x in the element tree, there is nothing here that deals with property TestName. If you want a property on your user control, then you have to define the property in the class corresponding to that user control (in your case it would be muc), and use {Binding RelativeSource={RelativeSource FindAncestor, ...}} to reference it on your user control (see here for details), or give it a name so you can use ElementName.

Resources