How can I Add a dependency property to a text box and bind the dependency property to a Boolean property in silver light. my Boolean property is in my view model.
ImageSearchIsFocused is the property which allows me to set the focus on a text box.
<TextBox Text="{Binding ImgSearch, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Behaviors>
<common:FocusBehavior HasInitialFocus="True" IsFocused="{Binding ImageSearchIsFocused, Mode=TwoWay}" ></common:FocusBehavior>
</i:Interaction.Behaviors>
</TextBox>
ImageIsFocused Property
bool _ImageSearchIsFocused;
public bool ImageSearchIsFocused
{
get { return _ImageSearchIsFocused; }
set
{
_ImageSearchIsFocused = value;
NotifyPropertyChanged("ImageSearchIsFocused");
}
}
If you want to add a dependency property, you're going to have the subclass the TextBox and add the dependency property to your subclass. Then you can bind that to whatever you like:
public class MyTextBox : TextBox
{
public static readonly DependencyProperty MyBooleanValueProperty = DependencyProperty.Register(
"MyBooleanValue", typeof(bool), typeof(MyTextBox),
new PropertyMetadata(new PropertyChangedCallback(MyBooleanValueChanged)));
public bool MyBooleanValue
{
get { return (bool)GetValue(MyBooleanValueProperty); }
set { SetValue(MyBooleanValueProperty, value); }
}
private static void MyBooleanValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var propValue = (bool)e.NewValue;
var control = d as MyTextBox;
// do something useful
}
}
Related
I have a custom WPF control (using UserControl as base) that exposes a bindable properties (using DependencyProperty). I want to disable editing in this control when one of the properties is a oneway binding.
public partial class OnOffControl : UserControl
{
...
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register(
"IsChecked",
typeof(bool?),
typeof(OnOffControl),
...
public bool? IsChecked
{
get
{
return (bool?)GetValue(IsCheckedProperty);
}
set
{
SetValue(IsCheckedProperty, value);
}
}
Usage point
<DataGridTemplateColumn Width="40" Header="State">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<UIUtil:OnOffControl
IndicatorType="SwitchIndicator"
IsChecked="{Binding Value, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
So when IsChecked is a oneway binding I want to disable editing in OnOffControl. How does one go about detecting the property binding is OneWay inside of the control and then disable editing?
You may check if there is a Binding and get the Binding's properties in a PropertyChangedCallback:
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register(
nameof(IsChecked),
typeof(bool?),
typeof(OnOffControl),
new PropertyMetadata(IsCheckedPropertyChanged));
public bool? IsChecked
{
get { return (bool?)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
private static void IsCheckedPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var control = (OnOffControl)o;
var binding = control.GetBindingExpression(IsCheckedProperty)?.ParentBinding;
var enabled = false;
if (binding != null)
{
enabled = binding.Mode == BindingMode.TwoWay
|| binding.Mode == BindingMode.OneWayToSource;
}
control.IsEnabled = enabled;
}
I have a datatemplate like this:
<DataTemplate DataType="{x:Type mvvm:ComponentViewModel}">
<v:UCComponents></v:UCComponents>
</DataTemplate>
UCComponent is a usercontrol with a public property called ID. ComponentViewModel also has a property called ID. I would like to set UCComponent ID property in the setter of the ViewModel's property. How can I do that?
This is what I've tried:
private string _ID;
public string ID
{
set { ((UCComponents)((DataTemplate)this).LoadContent()).ID = value; }
}
Error: this cannot be converted from ComponentViewModel to DataTemplate. Any help will be appreciated.
I'm not keeping true to MVVM design pattern, which is probably the reason for my frustration, but is there a way to access the UserControl used in the template?
Amanda
Thanks for the help, but it doesn't work. The getter and setter of ID is never called. This is my code:
public static DependencyProperty IDProperty = DependencyProperty.Register("ID", typeof(Guid), typeof(UCComponents));
public Guid ID
{
get
{ return (Guid)GetValue(IDProperty); }
set
{
SetValue(IDProperty, value);
LoadData();
}
}
I can't make the UserControl a DependencyObject. Is that perhaps the problem?
You can add a dependency property on your user control (UCComponents.xaml.cs) as follows
public static DependencyProperty IDProperty = DependencyProperty.Register(
"ID",
typeof(Object),
typeof(BindingTestCtrl));
public string ID
{
get
{
return (string)GetValue(IDProperty);
}
set
{
SetValue(IDProperty, value);
}
}
then you can bind it using
<DataTemplate DataType="{x:Type mvvm:ComponentViewModel}">
<v:UCComponents ID="{Binding ID}" />
</DataTemplate>
Another solution would be to handle the DataContextChanged event on your user control with something like
private ComponentViewModel _data;
private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
_data = e.NewValue as ComponentViewModel;
this.ID = _data.ID;
}
Thanks Jim,
I got it to work this way: In my UserControl:
public static DependencyProperty IDProperty = DependencyProperty.Register("ID", typeof(Guid), typeof(UCComponents));
public Guid ID
{
get
{ return (Guid)GetValue(IDProperty); }
set
{
SetValue(IDProperty, value);
LoadData();
}
}
private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
_data = e.NewValue as ComponentViewModel;
this.ID = _data.ID;
}
In my ViewModel (used as the template type):
public ComponentViewModel(Guid id)
{
DisplayName = "Component";
Glyph = new BitmapImage(new Uri("/Remlife;component/Images/Toolbox/Control.png", UriKind.Relative));
ID = id;
}
And in my Resources file:
<DataTemplate DataType="{x:Type mvvm:ComponentViewModel}">
<v:UCComponents ID="{Binding ID}"/>
</DataTemplate>
Somehow I need to still pass the ID to the ViewModel in it's constructor. Why? I'm not sure, but at least it works now. Thanks for your help.
I have a custom UserControl that contains a grid ...I wish to set the ItemsSource property of that grid by xaml code of of a data template in a resource dictionary...
then I have used dependency property... this is my implementation...
public partial class MyControlGrid : UserControl
{
// Dependency Property
public static readonly DependencyProperty MyItemSourceProperty =
DependencyProperty.Register("MyItemSource", typeof(ICollectionView),
typeof(MyControlGrid), new FrameworkPropertyMetadata(null, OnMyItemSourcePropertyChanged));
IDictionary<string, string> _columns = new Dictionary<string, string>();
private static void OnMyItemSourcePropertyChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
// When the color changes, set the icon color PlayButton
MyControlGrid muc = (MyControlGrid)obj;
ICollectionView value = (ICollectionView)args.NewValue;
if (value != null)
{
muc.MyGridControl.ItemsSource = value;
}
}
public ICollectionView MyItemSource
{
get
{
return (ICollectionView)GetValue(MyItemSourceProperty);
}
set
{
SetValue(MyItemSourceProperty, value);
//OnTargetPowerChanged(this, new DependencyPropertyChangedEventArgs(TargetPowerProperty, value, value));
// Old value irrelevant.
}
}
public MyControlGrid()
{
InitializeComponent();
}
}
this is the user control xaml code
<UserControl x:Class="GUI.Design.Templates.MyControlGrid"
Name="MyListControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfTkit="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns:Templates="clr-namespace:Emule.GUI.Design.Templates">
<StackPanel>
<WpfTkit:DataGrid ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Templates:MyControlGrid}}, Path=MyItemSource}"
x:Name="MyGridControl"
<StackPanel>
this is the binding path expression I use
<basic:MyControlGrid MyItemSource="{Binding MyDataContextVisibleCollection}"/>
this dont work and wpf output window dont show me any errors
note that, naturally, if I bind this directly in the user controls work fine
<WpfTkit:DataGrid ItemsSource="{Binding MyDataContextVisibleCollection}"
Waths I wrong?
thanks
p.s. sorry for my english
this
answer show me the way
use of PropertyChangedCallback work fine with my code:
public static readonly DependencyProperty MyItemSourceProperty =
DependencyProperty.Register("MyItemSource", typeof(IEnumerable),
typeof(MyControlGrid), new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(MyControlGrid.OnItemsSourceChanged)));
alternatively I have to remove comment on OnTargetPowerChanged and fire the property changed event
set
{
SetValue(MyItemSourceProperty, value);
//OnTargetPowerChanged(this, new DependencyPropertyChangedEventArgs(TargetPowerProperty, value, value));
// Old value irrelevant.
}
correct with
public ICollectionView MyItemSource
{
get
{
return (ICollectionView)GetValue(MyItemSourceProperty);
}
set
{
SetValue(MyItemSourceProperty, value);
OnItemsSourceChanged(this, new DependencyPropertyChangedEventArgs(MyItemSourceProperty, value, value));
}
}
I'm new to WPF and MVVM. I'm struggling to determine the best way to change the view of a chart. That is, initially a chart might have the axes: X - ID, Y - Length, and then after the user changes the view (either via lisbox, radiobutton, etc) the chart would display the information: X - Length, Y - ID, and after a third change by the user it might display new content: X - ID, Y - Quality.
My initial thought was that the best way to do this would be to change the bindings themselves. But I don't know how tell a control in XAML to bind using a Binding object in the ViewModel, or whether it's safe to change that binding in runtime?
Then I thought maybe I could just have a generic Model that has members X and Y and populate them as needed in the viewmodel?
My last thought was that I could have 3 different chart controls and just hide and show them as appropriate.
What is the CORRECT/SUGGESTED way to do this in the MVVM pattern? Any code examples would be greatly appreciated.
Thanks
Here's what I have for the bind to bindings method:
XAML:
<charting:Chart.Series>
<charting:BubbleSeries Name="bubbleSeries1"
ClipToBounds="False"
model:MakeDependencyProperty.IndependentValueBinding="{Binding AxisChoice.XBinding}"
model:MakeDependencyProperty.DependentValueBinding="{Binding AxisChoice.YBinding}"
model:MakeDependencyProperty.SizeValueBinding="{Binding AxisChoice.SizeBinding}"
IsSelectionEnabled="True" SelectionChanged="bubbleSeries1_SelectionChanged"
ItemsSource="{Binding Data}">
</charting:BubbleSeries>
</charting:Chart.Series>
<ComboBox Height="100" Name="listBox1" Width="120" SelectedItem="{Binding AxisChoice}">
<model:AxisGroup XBinding="{Binding Performance}" YBinding="{Binding TotalCount}" SizeBinding="{Binding TotalCount}" Selector.IsSelected="True"/>
<model:AxisGroup XBinding="{Binding ID}" YBinding="{Binding TotalCount}" SizeBinding="{Binding BadPerformance}"/>
<model:AxisGroup XBinding="{Binding ID}" YBinding="{Binding BadPerformance}" SizeBinding="{Binding TotalCount}"/>
</ComboBox>
AxisGroup:
public class AxisGroup : DependencyObject// : FrameworkElement
{
public Binding XBinding { get; set; }
public Binding YBinding { get; set; }
public Binding SizeBinding { get; set; }
}
DP:
public class MakeDependencyProperty : DependencyObject
{
public static Binding GetIndependentValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(IndependentValueBindingProperty); }
public static void SetIndependentValueBinding(DependencyObject obj, Binding value) { obj.SetValue(IndependentValueBindingProperty, value); }
public static readonly DependencyProperty IndependentValueBindingProperty =
DependencyProperty.RegisterAttached("IndependentValueBinding", typeof(Binding), typeof(MakeDependencyProperty), new PropertyMetadata { PropertyChangedCallback = (obj, e) => { ((BubbleSeries)obj).IndependentValueBinding = (Binding)e.NewValue;}});
public static Binding GetDependentValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(DependentValueBindingProperty); }
public static void SetDependentValueBinding(DependencyObject obj, Binding value) { obj.SetValue(DependentValueBindingProperty, value); }
public static readonly DependencyProperty DependentValueBindingProperty =
DependencyProperty.RegisterAttached("DependentValueBinding", typeof(Binding), typeof(MakeDependencyProperty), new PropertyMetadata { PropertyChangedCallback = (obj, e) => { ((BubbleSeries)obj).DependentValueBinding = (Binding)e.NewValue; } });
public static Binding GetSizeValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(SizeValueBindingProperty); }
public static void SetSizeValueBinding(DependencyObject obj, Binding value) { obj.SetValue(SizeValueBindingProperty, value); }
public static readonly DependencyProperty SizeValueBindingProperty =
DependencyProperty.RegisterAttached("SizeValueBinding", typeof(Binding), typeof(MakeDependencyProperty), new PropertyMetadata { PropertyChangedCallback = (obj, e) => { ((BubbleSeries)obj).SizeValueBinding = (Binding)e.NewValue; } });
}
ViewModel:
public class BubbleViewModel : BindableObject
{
private IEnumerable<SessionPerformanceInfo> data;
public IEnumerable<SessionPerformanceInfo> Data { ... }
public AxisGroup AxisChoice;
}
This generates the following exception:
+ $exception {"Value cannot be null.\r\nParameter name: binding"} System.Exception {System.ArgumentNullException}
Has something to do with the 4 bindings in teh bubbleSeries. I'm more than likely doing something wrong with binding paths but as I said I'm new to binding and wpf, so any tips would be greatly appreciated.
Your initial thought was correct: You can bind to bindings, for example if you want to change both axes together you might have a ComboBox like this:
<ComboBox SelectedItem="{Binding AxisChoice}">
<my:AxisChoice XBinding="{Binding ID}" YBinding="{Binding Length}" />
<my:AxisChoice XBinding="{Binding Length}" YBinding="{Binding ID}" />
<my:AxisChoice XBinding="{Binding ID}" YBinding="{Binding Quality}" />
</ComboBox>
To make this work you need to declare XBinding and YBinding as CLR properties of type "Binding":
public class AxisChoice
{
public Binding XBinding { get; set; }
public Binding YBinding { get; set; }
}
Ideally you could then simply bind the DependentValueBinding or IndependentValueBinding of your chart:
<Chart ...>
<LineSeries
DependentValueBinding="{Binding AxisChoice.XBinding}"
IndependentValueBinding="{Binding AxisChoice.YBinding}" />
</Chart>
Unfortunately this does not work because DependentValueBinding and IndependentValueBinding aren't DependencyProperties.
The workaround is to create an attached DependencyProperty to mirror each property that is not a DependencyProperty, for example:
public class MakeDP : DependencyObject
{
public static Binding GetIndependentValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(IndependentValueBindingProperty); }
public static void SetIndependentValueBinding(DependencyObject obj, Binding value) { obj.SetValue(IndependentValueBindingProperty, value); }
public static readonly DependencyProperty IndependentValueBindingProperty = DependencyProperty.RegisterAttached("IndependentValueBinding", typeof(Binding), typeof(MakeDP), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
((DataPointSeries)obj).IndependentValueBinding = (Binding)e.NewValue;
}
});
public static Binding GetDependentValueBinding(DependencyObject obj) { return (Binding)obj.GetValue(DependentValueBindingProperty); }
public static void SetDependentValueBinding(DependencyObject obj, Binding value) { obj.SetValue(DependentValueBindingProperty, value); }
public static readonly DependencyProperty DependentValueBindingProperty = DependencyProperty.RegisterAttached("DependentValueBinding", typeof(Binding), typeof(MakeDP), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
((DataPointSeries)obj).DependentValueBinding = (Binding)e.NewValue;
}
});
}
So your XAML becomes:
<Chart ...>
<LineSeries
my:MakeDP.DependentValueBinding="{Binding AxisChoice.XBinding}"
my:MakeDP.IndependentValueBinding="{Binding AxisChoice,YBinding}" />
</Chart>
If instead you want to change axes separately (two separate ComboBoxes or ListBoxes), you don't need AxisChoice: Simply make the Items or ItemsSource of each ComboBox consist of bindings, and put the a "XBinding" and "YBinding" properties directly in your view model.
Note that if your control exposes a regular property instead of a property of type Binding you can still use this method, but in this case you will use BindingOperations.SetBinding instead of just storing the binding value.
For example, if you want to change the binding of the text in a TextBlock from:
<TextBlock Text="{Binding FirstName}" />
to
<TextBlock Text="{Binding LastName}" />
based on a ComboBox or ListBox selection, you can use an attached property as follows:
<TextBlock my:BindingHelper.TextBinding="{Binding XBinding}" />
The attached property implementation is trivial:
public class BindingHelper : DependencyObject
{
public static BindingBase GetTextBinding(DependencyObject obj) { return (BindingBase)obj.GetValue(TextBindingProperty); }
public static void SetTextBinding(DependencyObject obj, BindingBase value) { obj.SetValue(TextBindingProperty, value); }
public static readonly DependencyProperty TextBindingProperty = DependencyProperty.RegisterAttached("TextBinding", typeof(BindingBase), typeof(BindingHelper), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
BindingOperations.SetBinding(obj, TextBlock.TextProperty, (BindingBase)e.NewValue)
});
}
I'm trying to simplify things so I made the ItemsSource of a ComboBox (Y1-Axis) consist of an observable collection of bindings, and I put the "YBinding" property directly in the ViewModel and set the public binding property as the combobox SelectedItem.
The the dependentvaluebinding is crashing the app though when using the public Binding SelectedY1:
<ComboBox Height="22" Name="comboBox1"
DisplayMemberPath="Source.MetricVarName"
ItemsSource="{Binding AllY1Choices}"
SelectedIndex="0"
SelectedItem="{Binding SelectedY1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</ComboBox>
<chartingToolkit:LineSeries
ItemsSource="{Binding AllY1Axis}"
IndependentValueBinding="{Binding AccumDate}"
my:MakeDP.DependentValueBinding="{Binding SelectedY1}">
In the VM:
private Binding _Y1axisChoice = new Binding();
private ObservableCollection<Binding> _allY1Choices = new ObservableCollection<Binding>();
public ObservableCollection<Binding> AllY1Choices
{
get { return _allY1Choices; }
set
{
_allY1Choices = value;
OnPropertyChanged("AllY1Choices");
}
}
private Binding _selectedY1 = new Binding();
public Binding SelectedY1
{
get { return _selectedY1; }
set
{
if (_selectedY1 != value)
{
_selectedY1 = value;
OnPropertyChanged("SelectedY1");
}
}
}
In the VM contstructor:
_Y1axisChoice = new Binding("MetricVarID");
_Y1axisChoice.Source = AllY1MetricVars[0];
_selectedY1 = _Y1axisChoice; // set default for combobox
_allY1Choices.Add(_Y1axisChoice);
_Y1axisChoice = new Binding("MetricVarID");
_Y1axisChoice.Source = AllY1MetricVars[1];
_allY1Choices.Add(_Y1axisChoice);
Any thoughts on this? The Binding object "SelectedY1" has Source.MetricID="OldA", and that's a valid value for the dependent value binding.
The error:
An exception of type 'System.InvalidOperationException' occurred in System.Windows.Controls.DataVisualization.Toolkit.dll but was not handled in user code
Additional information: Assigned dependent axis cannot be used. This may be due to an unset Orientation property for the axis or a type mismatch between the values being plotted and those supported by the axis.
Thanks
I tried to create a user control as:
public partial class MyTextBlock : UserControl
{
public MyTextBlock()
{
InitializeComponent();
}
public static readonly DependencyProperty LabelProperty
= DependencyProperty.RegisterAttached("Label", typeof(string), typeof(MyTextBlock), null);
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty MyTextProperty
= DependencyProperty.RegisterAttached("MyText", typeof(string), typeof(MyTextBlock), null);
public string MyText
{
get { return (string)GetValue(MyTextProperty); }
set { SetValue(MyTextProperty, value); }
}
}
And its xaml is:
<Grid x:Name="LayoutRoot">
<TextBlock x:Name="Title" Text="{Binding Label}" />
<TextBlock x:Name="MyText" Text="{Binding MyText}" TextWrapping="Wrap"/>
</Grid>
Want I want is trying to binding dependency property in this control to UI elements, so that when i use this control, I can set data binding like:
<local:MyTextBlock Label="{Binding ....}" MyText = "{Binding ....}" />
But When I did as above, it's not working. No data bound, no error. How to fix it?
Trying using .Register instead of .RegisterAttached on the DependencyProperty
You need to provide a callback to set the value
I think the 'int' type should be 'string'
putting it all together
public partial class MyTextBlock : UserControl
{
public MyTextBlock()
{
InitializeComponent();
}
public static readonly DependencyProperty LabelProperty
= DependencyProperty.Register("Label", typeof(string), typeof(MyTextBlock), new PropertyMetadata(new PropertyChangedCallback(LabelChanged)));
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
private static void LabelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var c = d as MyTextBlock;
if (c != null )
{
c.label.Text = e.NewValue as string;
}
}
}
Basically you just have to wrap those dependency properties in a class. Set the DataContext on your control to an instance of that class and bind away.