Avoid Two way binding between objects in C# - wpf

When i assign a value of a class object to other class object and when i change a property of 1st object it reflects in other object.
Example:
class A has property of type int Aid
SelectedA is a property of a Viewmodel Class which is binded to A WPF View.
SelectedA.Aid is in TwoWay binding with a combo box.
i have created another object objectA and assigned objectA=SelectedA.
Problem when i change combobox value value of objectA.Aid is also changed.
Thanks in advanced i need to avoid binding of objectA with SelectedA.
Vehicle dbvalue
private Vehicle _selectedA;
public Vehicle SelectedA
{
get { return _selectedA; }
set
{
_selectedA = value;
RaisePropertyChanged("SelectedA");
}
}
public partial class A
{
public int AID { get; set; }
public string AName { get; set; }
}
<ComboBox
DisplayMemberPath="AName"
ItemsSource="{Binding items}"
SelectedValue="{Binding SelectedA.AID}"
SelectedValuePath="AName "/>
In viewmodel Class
i have used
dbvalue = SelectedA;
When i change combobox value dbvalue.AID is also changed.

You can set the behaviour of your SelectedValue binding to only update when the source changes.
SelectedValue="{Binding SelectedA.AID, Mode=OneWay}"
These are the other possible values for the Binding Mode lifted from the MSDN site:
TwoWay updates the target property or the property whenever either the target property or the source property changes.
OneWay updates the target property only when the source property changes.
OneTime updates the target property only when the application starts or when the DataContext undergoes a change.
OneWayToSource updates the source property when the target property changes.
Default causes the default Mode value of target property to be used.
Original MSDN article

To prevent of this problem, you can set it each property of object as follows:
objectA.AId = SelectedA.AId
objectA.AName = SelectedA.AName
Good Luck

Related

Binding to an initially NULL property in the ViewModel doesn't rebind

I have a UserControl that contains a Telerik RadDataForm. The form's ItemsSource is bound to a property on the UserControl's ViewModel:
<telerik:RadDataForm
ItemsSource="{Binding Path=viewModel.items, RelativeSource={RelativeSource AncesterType=local:MyUserControl}}"
/>
Where viewModel is:
public partial class MyUserControl: UserControl
{
public MyUserControlVM viewModel
{ get { return this.DataContext as MyUserControlVM; } }
}
Within the viewmodel, items is a fairly ordinary collection:
public class MyUserControlVM : MyViewModelBase
{
private ObservableCollection<AnItem> items_;
public ObservableCollection<AnItem> items
{
get { return this.items_; }
set
{
this.items_ = value;
notifyPropertyChanged("items");
}
}
...
}
And where, of course, MyViewModelBase implements INotifyPropertyChanged.
The user control has an items dependency property, and when it is set, it sets the matching property on the view model:
public partial class MyUserControl : UserControl
{
public ObservableCollection<AnItem> items
{
get { return GetValue itemsProperty as ObservableCollection<AnItem>; }
set { SetValue(itemsProperty, value); }
}
public static readonly DependencyProperty itemsProperty =
DependencyProperty.Register("items",
typeof(ObservableCollection<AnItem>),
typeof(MyUserControl), new PropertyMetadata(
new PropertyChangedCallback(itemsPropertyChanged)));
private static void itemsPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
MyUserControl myUserControl = d as MyUserControl;
ObservableCollection<AnItem> items =
e.NewValue as ObservableCollection<AnItem>;
if (myUserControl != null && myUserControl.viewModel != null)
myUserControl.viewModel.items = items;
}
}
All of which seems pretty straightforward, if a bit tedious.
The problem is that the items dependency property on MyUserControl is bound to a property of the current item of another collection, and that the current item is initially null, and so when MyUserControl is initially loaded, its items property is null. And hence, so is the items property on MyUserControlVM that the RadDataForm is binding to.
Later, when an item in that outer collection is made current, the items dependency property on MyUserControl is set, and that sets the items property on MyUserControlVM. And MyUserControlVM calls notifyPropertyChanged so that listeners will be informed of the change. But this last is not working.
Afterwards, if I examine RadDataForm, its ItemsSource property is still null.
It's like the RadDataForm isn't listening for the propertychanged event, because what it was bound to was initially null. In similar circumstances where the bound property is not null at the start, this pattern works fine as the current item changes from one item to another, but it doesn't seem to work from having no current item to having one.
So, any ideas as to how to make this work? I can't, given the circumstances, make it so that items always has a value when the form loads - it is always going to be null, at the start. How do I get the RadDataForm to notice when the property becomes non-null?
When I want to reference something at the root of my UserControl (a custom property, for instance, or as in your case, the DataContext), I usually give my UserControl a Name. Then I use this name together with the ElementName property on the Binding to set it up.
<UserControl
...
Name="TheControl">
<Grid>
<TextBlock Text={Binding Path=DataContext.items, ElementName=TheControl}" />
</Grid>
</UserControl>
Due to the viewModel property, you can use that and DataContext interchangeably.
However, in your case it might actually be simpler. First, there's a typo in your code. It should be AncestorType (with an 'o'). Second, you might want to try setting up the binding using only {Binding Path=items} since I believe that your control already inherits the correct DataContext. (Not sure about that last one, though.)
If the problem persists, and you suspect that it does in fact has something to do with the items property returning null initially, you could always initialize the items_ with an empty collection to avoid the null.
private ObservableCollection<AnItem> items_ = new ObservableCollection<AnItem>();

ComboBox Binding Collection and Selected Property

Why doesn't #2 work? (It seems like most examples say to do this).
#1(works)<ComboBox ItemsSource="{Binding Marker.ReadOnlyContentRegions}"
SelectedItem="{Binding Marker.SelectedRegion}"
SelectedValue="{Binding
Marker.SelectedRegion.UniqueId, Mode=TwoWay}"
SelectedValuePath="UniqueId"
DisplayMemberPath="Label" />
#2(doesn't work)<ComboBox ItemsSource="{Binding Marker.ReadOnlyContentRegions}"
SelectedValue="{Binding Marker.SelectedRegion.UniqueId,Mode=TwoWay}"
SelectedValuePath="UniqueId"
DisplayMemberPath="Label" />
This is the class that contains the objects that should be databound.
class...
public CancellableObservableCollection<InvisibleContentMarkerBase>
ReadOnlyContentRegions
{
get { return
CancellableObservableCollection<InvisibleContentMarkerBase>)
GetValue(ReadOnlyContentRegionsProperty); }
set { SetValue(ReadOnlyContentRegionsProperty, value); }
}
public static readonly DependencyProperty ReadOnlyContentRegionsProperty =
DependencyProperty.Register("ReadOnlyContentRegions",
typeof(CancellableObservableCollection<InvisibleContentMarkerBase>),
typeof(TargetedContentMarker), new UIPropertyMetadata(null);
public InvisibleContentMarkerBase SelectedRegion
{
get { return (InvisibleContentMarkerBase)GetValue(SelectedRegionProperty); }
set { SetValue(SelectedRegionProperty, value); }
}
public static readonly DependencyProperty SelectedRegionProperty =
DependencyProperty.Register("SelectedRegion",
typeof(InvisibleContentMarkerBase), typeof(TargetedContentMarker), new
UIPropertyMetadata(null));
...// end of class
First of all, don't set both the SelectedItem and the SelectedValue
They both set the exact same property, so when you set both only one value will actually get used
When you set SelectedValuePath and SelectedValue, you are setting the selected item by value. The SelectedValuePath tells WPF what property on objects in the collection is the Id field, and SelectedValue tells WPF to set the selected item to the value that is equal to SelectedValue.
SelectedItem simply tells WPF to select the item in the collection that exactly matches the SelectedItem object. Note that this comparison is by reference, so if the SelectedItem is a class that doesn't point to the exact same reference in memory as one of the objects in the ItemsSource, it won't evaluate the two objects as the same and won't set the item as Selected
So in short, either get rid of the SelectedItem binding and just use SelectedValue/SelectedValuePath, or remove SelectedValue/SelectedValuePath and ensure that the object bound in SelectedItem refers to the exact same object in memory as the copy in the ItemsSource.
If you really can't reference that object, and insist on using SelectedItem instead of SelectedValue, you could also overwrite the .Equals() on your class so it returns true if the data is equal, regardless of if the memory reference is the same. I prefer to avoid this since this changes the functionality of any instances of this class, but wanted to let you know that option is available.

Silverlight UI element's 1-time binding to nested non-dependency property fails

I want a 1-time databind between a checkbox (IsChecked propety) in a childWindow and a nested member variable in it's DataContext object, such that only the initial IsChecked property is set by the member variable. The member variable (binding source) is not INotifyPropertyChanged or marked as a DependencyProperty- but I think that is ok b/c I only want it to be evaluated once- when it gets its initial value.
Binding (in testChildWindow.xaml):
<CheckBox Content="Show Username?" Name="cbShowUser" IsChecked="{Binding Path=User.showUser}"/>
Setting DataContext (in parent window code-behind):
testChildWindow dlgBox = new testChildWindow();
dlgBox.DataContext = (this.DataContext as IAssignDlgViewModel).AssignVM("defaultChildWindow");
dlgBox.Show();
Data Context/Member variable:
public class testChildWindowViewModel : IDlgViewUpdate
{
public User
...
}
public class User
{
public bool showUser;
public User()
{
showUser = true;
}
...
}
If I make the Vm's binding source property (showUser) a dependency property at the (non-nested) testChildWindowViewModel, then the binding works. But all other combinations seem to fail.
Why must it be a dependency (or INotifyPropertyChanged?) property for a 1-time binding?
Why can't I get it to work at a nested level?
Thanks!!!
Ah, looking at the Output window during the binding answered the question for me. The problem was that User was not a property. Changed it to an auto property and the binding works just right now.

WPF derived ComboBox SelectedValuePath issue

In our application we have a very large set of data that acts as our data dictionary for ComboBox lists, etc. This data is staticly cached and keyed off of 2 variables, so I thought it wise to write a control that derived from ComboBox and exposed the 2 keys as DPs. When those 2 keys have proper values I set the ItemsSource of the ComboBox automatically from the data dictionary list it corresponds to. I also automatically set the SelectedValuePath and DisplayMemberPath in the constructor to Code and Description, respectively.
Here's how an example of how an item in the ItemsSource from the data dictionary list always looks:
public class DataDictionaryItem
{
public string Code { get; set; }
public string Description { get; set; }
public string Code3 { get { return this.Code.Substring(0, 3); } }
}
The value of Code is always 4 characters long, but sometimes I only need to bind 3 characters of it. Hence, the Code3 property.
Here's how the code looks inside my custom combobox to set the ItemsSource:
private static void SetItemsSource(CustomComboBox combo)
{
if (string.IsNullOrEmpty(combo.Key1) || string.IsNullOrEmpty(combo.Key2))
{
combo.ItemsSource = null;
return;
}
List<DataDictionaryItem> list = GetDataDictionaryList(combo.Key1, combo.Key2);
combo.ItemsSource = list;
}
Now, my problem is, when I change the SelectedValuePath in the XAML to Code3, it doesn't work. What I bind to SelectedValue still gets the full 4 character Code from DataDictionaryItem. I even tried rerunning SetItemsSource when the SelectedValuePath was changed and no dice.
Can anyone see what I need to do to get my custom combobox to wake up and use the SelectedValuePath provided if it's overridden in the XAML? Tweaking the value in the property setter in my SelectedValue bound business object is not an option.
Here's how the XAML looks for my combobox in a form:
<c:CustomComboBox Key1="0" Key2="8099" SelectedValuePath="Code3" SelectedValue="{Binding Thing}"/>
EDIT: I just ran snoop on my code and it says my SelectedValuePath is Code... it doesn't appear to ever be set to Code3... Zuh?
Ok, I figured it out.
Apparently setting the default values of a DependencyProperty in the default non-static constructor of a WPF control is a no-no. So, at first I tried this:
static ValueCodeListComboBox()
{
SelectedValuePathProperty.OverrideMetadata(typeof(ValueCodeListComboBox), new PropertyMetadata("Code"));
DisplayMemberPathProperty.OverrideMetadata(typeof(ValueCodeListComboBox), new PropertyMetadata("Description"));
}
But this kept throwing an error saying:
Metadata override and base metadata must be of the same type or derived type.
Finally figured out that meant I needed to use FrameworkPropertyMetadata instead of PropertyMetadata:
static ValueCodeListComboBox()
{
SelectedValuePathProperty.OverrideMetadata(typeof(ValueCodeListComboBox), new FrameworkPropertyMetadata("Code"));
DisplayMemberPathProperty.OverrideMetadata(typeof(ValueCodeListComboBox), new FrameworkPropertyMetadata("Description"));
}
Now changing SelectedValuePath in the XAML works great.

TextBox binding in WPF

I have a textbox in XAML file, On Changing the text in the textbox the prop setter is not getting called. I am able to get the value in ProjectOfficer textbox and not able to update it. I am using MVVM pattern
Below is my code XAML
TextBox Text="{Binding Path=Officer,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
x:Name="ProjectOfficer"/>
ViewModel.cs
public Staff Officer
{
get
{
return __con.PrimaryOfficer ;
}
set
{
_con.PrimaryOfficer = value;
_con.PrimaryOfficer.Update(true);
}
}
Staff.cs
public class Staff : EntityBase
{
public Staff();
public string Address { get; }
public string Code { get; set; }
public override void Update();
}
Thanks
You're binding a property of type string on the TextBox to a property of type Officer on your ViewModel. I expect the setter isn't being called because WPF can't do the conversion.
If you check the output window in visual studio, you'll probably see a binding error for this property.
Try something like:
TextBox text ="{Binding Path=Address,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
x:Name="ProjectOfficer"/>
Make sure the holder of the TextBox is linked to a Staff object. The textbox cannot bind directly to an object without telling the property to display (like Address in my example above).

Resources