I have a generic custom control that contains a property as seen below:
public T Editing
{
get { return _editing; }
set
{
if (object.ReferenceEquals(value, _editing))
return;
BeginInvoke(new MethodInvoker(() => UpdateOnGuiThread(value)));
}
}
I have tried databinding this property to a property on my controller object as seen below:
_customCtrl.DataBindings.Add(new Binding("Editing", _controller, "CurrentItem"));
The controller class implements INotifyPropertyChanged and exposes the property like this:
public SpecialData CurrentItem
{
get { return _currentItem; }
set
{
_currentItem= value;
OnPropertyChanged("CurrentItem");
}
}
But as I broadcast PropertyChanged from my controller class the debugger will never enter in the setter of the Editing property. I've also tried the databinding below to no avail.
_customCtrl.DataBindings.Add(new Binding("Editing", _controller, "CurrentItem", false, DataSourceUpdateMode.OnPropertyChanged));
I've read https://msdn.microsoft.com/en-us/library/ms233813(v=vs.80).aspx and tried using the DefaultBindingPropertyAttribute but it doesn't help either.
Does anybody know how to get this binding to work? I thought that the binding manager would propagate the value from the controller to the control's property as PropertyChanged is broadcast in the controller class (just like a simple textbox text binding).
Any ideas?
Thanks,
Sean
You should always create your bindings with FormattingEnabled property set to true. Note that FormattingEnabled=false is just for backward compatibility and unfortunately is the default.
Either use one of the Binding constructor (or ControlBindingsCollection.Add) overloads that have a formattingEnabled parameter or set the property afterwards. In your case, something like this
_customCtrl.DataBindings.Add("Editing", _controller, "CurrentItem", true);
Related
I am using data bindings in my Winforms application to connect the UI elements to an underlying object. My UI has several ComboBoxes in it, each pertaining to a different property of the class. Take the following simplified code, for example:
Object which implements INotifyPropertyChange
class MyObject : INotifyPropertyChange
{
// Custom logic in setters calls OnPropertyChanged
public int Property1 { get; set; }
public string Property2 { get; set; }
}
Assigning the data source
BindingSource MyDataSource = new BindingSource(this.components);
MyDataSource.Add(instanceOfMyObject);
Creating bindings
Binding binding1 = new Binding("SelectedIndex", MyDataSource, "Property1",
false, DataSourceUpdateMode.OnPropertyChanged);
binding1.Format += FormatForBinding1();
comboBox1.DataBindings.Add(binding1);
Binding binding2 = new Binding("SelectedIndex", MyDataSource, "Property2"
false, DataSourceUpdateMode.OnPropertyChanged);
binding2.Format += FormatForBinding2();
comboBox2.DataBindings.Add(binding2);
My issue is that when comboBox2.SelectedIndex changes, both Format handlers are called, which is not desired, when I only want FormatForBinding2() to be invoked. Is there a way to solve this? Am I misunderstanding something about bindings in that I cannot have multiple bindings with the same property name and the same data source, although the controls are different?
I think the issue was with the code being executed in the Format handlers. The Format handler should only be used to do what it's name implies -- format the value. Adding extra logic can lead to undesirable behavior, as I found out. One thing to consider is, along with the Format handler, also including a SelectedIndexChanged or SelectedValueChanged handler for the desired ComboBox and doing the other logic in there.
I have a user control with a dependency property:
public ObservableCollection<Exclusion> SelectedExclusions
{
get
{
return (ObservableCollection<Exclusion>)GetValue(SelectedExclusionsProperty);
}
set
{
SetValue(SelectedExclusionsProperty, value);
}
}
public static readonly DependencyProperty SelectedExclusionsProperty =
DependencyProperty.Register(nameof(TimeSeriesChart.SelectedExclusions),
typeof(ObservableCollection<Exclusion>),
typeof(TimeSeriesChart),
new PropertyMetadata(default(ObservableCollection<Exclusion>)));
I am adding a selected exclusion to this collection on key down:
protected override void OnKeyDown(KeyEventArgs e)
{
if(e.Key == Key.Delete)
{
this.SelectedExclusions.Add(this.ExclusionProviders[0].Exclusions[this.hitTestInfo.DataSeriesIndex]);
}
}
In the view model I have this property & backing variable:
private ObservableCollection<TimeSeriesLibraryInterop.Exclusion> selectedExclusionsToDelete = new ObservableCollection<TimeSeriesLibraryInterop.Exclusion>();
public ObservableCollection<TimeSeriesLibraryInterop.Exclusion> SelectedExclusionsToDelete
{
get
{
return this.selectedExclusionsToDelete;
}
set
{
this.selectedExclusionsToDelete = value;
this.RaisePropertyChanged();
}
}
Finally the binding in the view:
<userControl1 SelectedExclusions="{Binding SelectedExclusionsToDelete, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The dependency property collection is initialised and populated however the view model property setter is never hit when the dependency property collection changes (Add). I have no binding errors in the output window. Is there something I'm missing here?
Looks like you're adding an item to the collection rather than replacing the collection. You won't hit the vm collection property's setter that way.
If you want to your viewmodel to respond to items being added to the SelectedExclusionsToDelete collection, the viewmodel will need to handle the SelectedExclusionsToDelete.CollectionChanged event. "Properly" handling that event (remove, add, move, clear, etc.) is a real hassle, but if it's not a giant collection you can often get away with something quick and dirty: Treat any change as a whole new collection. I think that's exactly the case you've got, too.
Alternatively, for an even quicker and dirtier approach, I think you could make it a two-way binding by default and have the control assign a new ObservableCollection to this.SelectedExclusions in OnKeyDown. The binding will pass it back to the viewmodel and hit the setter.
I need to bind a property to a ToolStripMenuItem. I've searched around and found it impossible, the best workaround seems to be creating a BindableToolStripMenuItem class and implement it yourself. So I've taken some reasonably well established code from the internet:
public class BindableToolStripMenuItem : ToolStripMenuItem,IBindableComponent
{
private BindingContext bindingContext;
private ControlBindingsCollection dataBindings;
[Browsable(false)]
public BindingContext BindingContext
{
get
{
if (bindingContext == null)
bindingContext = new BindingContext();
return bindingContext;
}
set
{
bindingContext = value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ControlBindingsCollection DataBindings
{
get
{
if (dataBindings == null)
dataBindings = new ControlBindingsCollection(this);
return dataBindings;
}
}
}
Now all I need is to bind it right? I set up a form with a toolStripMenu and added a property to bind to: No luck. I can check it, but the changes don't seem to filter down into the binding.
public partial class Form1 : Form
{
private Boolean _BindingChecked;
public Boolean BindingChecked {
get { return _BindingChecked; }
set { _BindingChecked = value; Console.WriteLine(": " + _BindingChecked); }
}
public Form1()
{
InitializeComponent();
BindableToolStripMenuItem btsmi = new BindableToolStripMenuItem();
btsmi.Text = "Checkable";
btsmi.CheckOnClick = true;
btsmi.DataBindings.Add(new Binding("Checked",this,"BindingChecked"));
itemsToolStripMenuItem.DropDownItems.Add(btsmi);
}
}
It's definitely doing some of the binding though! If I change "Binding Checked" or "Checked" strings it throws the appropriate error eg:
An unhandled exception of type 'System.ArgumentException' occurred in System.Windows.Forms.dll
Additional information: Cannot bind to the property or column BindingChecked2 on the DataSource.
It's just any changes (to either, I tested it implementing InotifyChanged going the other way too) don't get applied to the bound property.
Any Idea where I've gone wrong? I've cut things down so much, it's just these two classes, and I've tried starting from scratch with the BdindableToolStripMenuItem, and looked at a fair few other people's implementation (almost all identical) and still can't ever get it to work.
Thanks
You probably want to set the DataSourceUpdateMode of the Binding to OnPropertyChanged, the default is OnValidation but since menu items don't have normal focus behavior I can imagine the default does not work. This allows updates on the menu item to propagate to the BindingChecked property
If you want the reverse, updates to BindingChecked property by code to propagate to the menu item, you must implement INotifyPropertyChanged on the form and raise the event whenever you change the property from code (e.g. in the property set accessor).
I've got a ListView which is bound to a list of objects. When I select an item in the ListView, I catch a SelectionChanged event and then pass the selected object off to a details view.
protected void list_selectionChanged(object sender, SelectionChangedEventArgs e) {
var myObject = theList.SelectedItem as MyObjectType;
detailsView.DataContext = myObject;
}
detailsView is a UserControl in the same WPF as the ListView. It contains some XAML like so:
<Label Content="{Binding Path=deviceId}"></Label>
<l:MyUc deviceId="{Binding Path=deviceId}" />
Inside MyUC, I've got a DependencyProperty defined:
public static readonly DependencyProperty deviceIdProperty = DependencyProperty.Register("deviceId", typeof(Guid), typeof(MyUC), new FrameworkPropertyMetadata(null));
public Guid deviceId {
get { return (Guid)GetValue(deviceIdProperty); }
set { SetValue(deviceIdProperty, value); }
}
The Label shows the deviceId, but the property inside MyUC never gets set.
Can anyone spot my mistake?
When you use a Dependency Property in XAML, the set method never gets called. If you want to "see" this set, you need to add a property changed callback, as the binding mechanism directly sets the dependency property without your setter.
For details on how to implement this, see the PropertyChanged callbacks section of MSDN.
First, it would be helpful if you could add the actual XAML code where you define the ListView and it's properties.
Second, you should look at the output console (in Visual Studio debug session of course) and see whether there are binding errors regarding the bindings you defined.
It is very probable that the bindings provide values that does not fit the deviceId dependency property type and thus it never changes.
[Category("SomeCat")]
[Description("Gets or sets how items are displayed in the ShellListView control.")]
[DefaultValue(View.Details)]
new public View View
{
get { return base.View; }
set
{
System.Diagnostics.Debug.WriteLine("View");
if (value != View.LargeIcon)
{
//Reset these values because they can only be true if LargeIcon is set.
ShowExtraLargeIcons = false;
}
base.View = value;
}
}
private bool m_ShowExtraLargeIcons;
[Category("Appearance")]
[DefaultValue(false)]
public bool ShowExtraLargeIcons
{
get { return m_ShowExtraLargeIcons; }
set
{
if (m_ShowExtraLargeIcons == value)
return;
System.Diagnostics.Debug.WriteLine("Extra");
m_ShowExtraLargeIcons = value;
if (m_ShowExtraLargeIcons)
// Always set view to LargeIcon if ShowExtraLargeIcons is enabled
View = View.LargeIcon;
}
}
My problem: If I set View to something else than LargeIcons (via the property manager of VS 2010), the ShowExtraLargeIcons-property remains True although it has been set to False.
If I set the ShowExtraLargeIcons to True, the property View is set to LargeIcons as expected.
Something that might help: The Debug-messages ("View" and "Extra") after setting ShowExtraLargeIcons are shown, after setting View they are not (both set during design time).
This has nothing to do with dependency properties, it's simply the behavior of the property browser.
When you use the new modifier on a class member, you are not creating an "override". ListView.View is not a virtual property. You are creating a completely new property (MyListView.View) which has the same signature and name.
The property browser is going to enumerate properties and use descriptors to work with them. It will see two completely different properties and either display both of them, or pick one arbitrarily.
new public View
Looks like you are editing some parant object if trace is not shown. And that paranet object is edited without influencing m_ShowExtraLargeIcons var.