I have a MVVM Windows Phone 8 app. The XAML page has a user control that I created that needs to be notified when a change takes place in the View Model. To facilitate this, I created an int property in the user control to be bound to a property in the View Model, so the user control property's setter method would be triggered when the property it was bound to in the View Model changed.
Using the code below, the user control's VideosShownCount property does show up in the Property List at design-time but when I click on the binding mini-button, the Create Data Binding option is greyed out in the pop-up menu.
So I have one or two questions, depending on what is the root problem:
1) How do I make a property in a View Model available as a Data Binding source?
2) How do I format a user control property so the IDE allows it to be data bound to a View Model property?
private int _videosShownCount = 0;
public int VideosShownCount
{
get
{
return this._videosShownCount;
}
set
{
this._videosShownCount = value;
}
}
public static readonly DependencyProperty VideoShownCountProperty =
DependencyProperty.Register("VideosShownCount", typeof(int), typeof(MyUserControl),
new PropertyMetadata(0, new PropertyChangedCallback(VideoShownCountPropertyChanged)));
static void VideoShownCountPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
MyUserControl MyUserUserControl = (MyUserControl)sender;
// Don't care about the value, just want the notification.
// int val = (int)e.NewValue;
// Do work now that we've been notified of a change.
MyUserUserControl.DoWork();
}
You're not using the DependencyProperty for your property, which will definitely cause problems between your code and the bindings
public int VideosShownCount
{
get { return (int) GetValue(VideosShownCountProperty); }
set { SetValue(VideosShownCountProperty, value); }
}
I'm not sure if this is the main cause of your problem, but it's worth fixing regardless.
Related
I'm trying to data bind a Slider control to a custom view model, and then bind the same property from the view model to a custom type that inherits from DependencyObject. The binding mode between the view model and the Slider control is two way, but the mode between the view model and the custom DependencyObject type should only be one way (the type should not be able to change the view model value).
Here's the relevant bit from my view model:
public class ScanViewModel : DependencyObject
{
public static readonly DependencyProperty CurrentScanIndexProperty = DependencyProperty.Register("CurrentScanIndex", typeof(Int32), typeof(ScanViewModel), new PropertyMetadata(0));
public Int32 CurrentScanIndex
{
get { return (Int32)GetValue(CurrentScanIndexProperty); }
set { SetValue(CurrentScanIndexProperty, value); }
In my XAML I bind the slider control as follows:
<Slider x:Name="scanIndexSlider" Minimum=0 Maximum = 100 Value="{Binding CurrentScanIndex, Mode=TwoWay, Delay=5}"
I have a 3rd object that participates as well:
public class CustomIndicator : DependencyObject
{
public static readonly DependencyProperty ScanIndexProperty = DependencyProperty.Register("ScanIndex", typeof(Int32), typeof(CustomIndicator), new PropertyMetadata(0));
public Int32 ScanIndex
{
get { return (Int32)GetValue(ScanIndexProperty); }
set { SetValue(ScanIndexProperty, value); }
}
public CustomIndicator(ScanViewModel ViewModel)
{
// Data bind to the view model programmatically:
Binding binding = new Binding("CurrentScanIndex");
binding.source = ViewModel;
binding.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(this, CustomIndicator.ScanIndexProperty, binding);
}
I assign an instance of "ScanViewModel" as the DataContext to the view containing the slider and the binding works i.e. I manipulate the slider and the dependency property on the view model changes to reflect the new slider value. However, the view models' new value is not then passed on to the "CustomIndicator.ScanIndex" dependency property that it was bound to during the CustomIndicator constructor method. If I run through step by step I can see the binding in the constructor seems to work initially...after the programmatic bindg in customIndicator contructor is executed the objects' "ScanIndex" reflects the same value as the view model to which it was just bound, so the binding works initially. However, "ScanIndex" on "CustomIndicator" never changes after that initial change. It's as if the binding works once (in the constructor) and then never again after that. As I mentioned, the binding between the Slider control and the view model works fine.
I should add that after instantiation the "CustomIndicator" object is then added to the Children collection of a custom UserControl that has its own DataContext (a different type). Could this be the problem?
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.
I have a list of items with id and description(i can introduce key-value collection instead if needed). What i need is control that binded to viewmodel id property, but shows description of corresponding item/pair on it. Closest example i know is combobox, where i set DisplayMemberPath and SelectedValue/SelectedValuePath, but i don't need dropdown. So is there any in-built control in Silverlight for this?
(of course i can code one myself, it's easy and I can even just put some logic for viewmodel to get pair i need and bind it's description to simple textblock)
Edit: To illustrate what funcionality i need i coded simple example class. It actually satisfies my needs, but i still want to know if i can use built-in control.
public class CollectionItemDisplayControl:TextBox
{
public CollectionItemDisplayControl()
{
IsReadOnly = true;
}
public string SelectedID
{
get { return (string)GetValue(SelectedIDProperty); }
set { SetValue(SelectedIDProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedID. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedIDProperty =
DependencyProperty.Register("SelectedID", typeof(string), typeof(CollectionItemDisplayControl), new PropertyMetadata(new PropertyChangedCallback(OnSelectedIDChangedStatic)));
private static void OnSelectedIDChangedStatic(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CollectionItemDisplayControl originator = d as CollectionItemDisplayControl;
if (originator != null)
{
originator.OnSelectedIDChanged(e);
}
}
private void OnSelectedIDChanged(DependencyPropertyChangedEventArgs e)
{
string description = String.Empty;
string value = e.NewValue as string;
if (value != null)
{
foreach (var item in _items)
{
if (item.UniqueID == value)
{
description = item.Description;
break;
}
}
}
Text = description;
}
private IDataCollection _viewModel;
public IDataCollection ViewModel
{
get { return _viewModel; }
set
{
_viewModel = value;
if (_viewModel != null)
{
_items = _viewModel.Items;
}
}
}
private ObservableCollection<IUnique> _items = new ObservableCollection<IUnique>();
}
ItemClass contains two properties: ID and Description. I can place this control on the page, bind Items, and one-way bind SelectedID.
Edit 2: well i didn't make SelectedID DependencyProperty so binding won't work, but i will fix it right away
Edit 3: first snippet was sloppy and didn't work properly, so i fixed it.
If I understood properly,
You just need the right binding implemented.
(you do need a list? not just a single item, even if single it's similar just any control)
Bind the list to e.g. ItemsControl.
Set ItemsSource to your list of items
Then override ToString on your Item providing it's 'yours' really. If not you can make your own wrapper.
Within ToString output whatever is presenting your item, e.g. description.
That's a quickest solution, you can also make item template as you want.
EDIT:
well just put everything in the view model and bind to it - the TextBox, i.e.
Text={Binding SelectedText}
e.g.
...in your view model add SelectedText and SelectedID (and Items if needed) - properly do OnPropertyChanged.
Set SelectedID from view model or if 'bound' from another control that may change it.
Within set for SelectedID set the SelectedText.
No need for a control for things like that, it's all data binding really.