In my Silverlight project I am creating textboxes which are two-way databound to some Context during runtime. The binding in one direction (from the source to the target) seems to work fine, but the other direction (from the target back to the source) is not showing any effect.
This is the data-context:
public class Leg : INotifyPropertyChanged {
private string passengers;
public string Passengers {
get { return passengers; }
set {
// here I have a breakpoint.
passengers = value;
FirePropertyChanged("Passengers");
}
}
private void FirePropertyChanged (string property) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then on another place I am creating a new TextBox control together with a binding for it:
Binding passengersBinding = new Binding();
// viewModelLeg is an instance of the class Leg from above
passengersBinding.Source = viewModelLeg;
passengersBinding.Path = new PropertyPath("Passengers");
passengersBinding.Mode = BindingMode.TwoWay;
legItem.paxTextBox.SetBinding(TextBox.TextProperty, passengersBinding);
Now when I am altering the value of the Passengers string the corresponding textbox that is bound to it is updating its text correctly. So here's everthing fine.
But when i change the text of a textbox manually and then make the textbox lose its focus, nothing happens - i.e. there is no two-way binding taking place - no down propagation of the new text-value of the textbox to the source !
I have a breakpoint at the setter of the passengers-attribute (marked with the breakpoint-comment above). When I am getting all this right the binding engine also uses this public setter when the target-value of a binding has changed to update the source - so when this happens the breakpoint must be hit. But he doesn't ! So it seems that i can do what I want with my textbox (play with the focus or press enter) it is never updating its source.
Am I overseeing something ? There must be a capital error either in my code or in my thinking.. i would be really thankful for any ideas ...
EDIT:
In the following I try to demonstrate how i create my XAML objects and my DataContext objects. Because I am creating XAML controls and their bindings at runtime I haven't found a good solution to implement the MVVM approach very well. So I am doing the following (which is maybe not the best way to do it):
The situation I am modelling is that I have a UserControl (called LegItem) which is comprised (primarely) of textboxes. At runtime the user can create as much of these userControls as hew wishes to (one after the other).
On my ViewModel side I have a class (called Leg) that serves as a ViewModel for exactly one LegItem. So when I have say n (XAML-) LegItems then I also have n Leg instances. I store these Leg objects in a List.
So I am doing the following everytime the user clicks the 'add a new leg' button:
// here we are inside the applications view in an .xaml.cs file
public void AddLeg () {
// this is going to serve as the ViewModel for the new LegItem
// I am about to create.
Leg leg = viewModel.insertLeg();
// here I am starting to create the visual LegItem. The ViewModel object
// I have created in the previous step is getting along with.
createLegItem(leg);
}
// the primary job here is to bind each contained textbox to its DataContext.
private LegItem createLeg (Leg viewModelLeg) {
// create the visual leg item control element
// which is defined as a XAML UserControl.
LegItem legItem = new LegItem();
Binding passengersBinding = new Binding();
// viewModelLeg is an instance of the class Leg from above
passengersBinding.Source = viewModelLeg;
passengersBinding.Path = new PropertyPath("Passengers");
passengersBinding.Mode = BindingMode.TwoWay;
legItem.paxTextBox.SetBinding(TextBox.TextProperty, passengersBinding);
}
// on the viewModel side there is this simple method that creates one Leg object
// for each LegItem the View is creating and stores inside a simple list.
public Leg InsertLeg () {
Leg leg = new Leg();
legList.add(leg)
return leg;
}
New Answer
Since you mentioned your binding was actually to a custom UserControl and not actually a TextBox, I would suggest looking into the XAML of your UserControl and making sure it is binding the data correctly
Old Answer
I did a quick test with a new Silverlight project and noticed that the startup project is SilverlightApplication1.Web, not SilverlightApplication.
This means that the breakpoint in the setter won't actually get hit when I run the project. You'll notice the breakpoint circle is just the outline, and the color isn't filled in. If you hover over it, it will say
The breakpoint will not currently be hit. No symbols have been loaded
for this document
If I start SilverlightApplication1 instead of the .Web version, the breakpoint gets hit.
The property is getting changed correctly regardless of which version I startup, however the breakpoint isn't getting hit if I start the project with the .Web version. I suspect this is your issue.
Related
Edit: i have simplified this problem a lot since i first wrote it up. See the code examples at the bottom. I will clean the post up tomorrow.
I want to set the DataContext AFTER the user as filled out a form because that is when I know the type of object I need to create (based on what the user selected). The problem with doing it this way is that the BindingGroup.IsDirty flag is false because all edits are done by this point and I suppose that is why my call to BindingGroup.UpdateSources is having no effect.
Essentially this is what I'm doing:
Have user fill out form. The DataContext has not been set yet, but bindings are in place.
In the buttonSave_Click handler, create and set the correct DataContext object (based on provided user input) then call BindingGroup.UpdateSources and close the dialog.
I realize there are other ways to go about this. Probably even better ones, but I am really just experimenting with DataBinding and trying to learn it better. That is why I am trying out many possible designs with this.
Ultimately I will probably settle on a design where I ask the questions that I need answered in order to know what object to create for the data context then set it and let them fill out the remainder of the form from there. But for now I want to get it working the other way (if it is even possible) just for learning purposes.
EDIT 1:
I have determined that the IsDirty flag must not be my problem because immediately before the DataContext is set, IsDirty is true. Then immediately after DataContext is set it becomes false so I suppose it automatically updated sources then, however, I do not see the changes reflected in my data object so it obviously failed for some reason.
I know my Bindings are correct because if I set the DataContext in the Windows ctor it updates the data. Move those two lines of code to the buttonSave_Click handler and it no longer updates data.
I feel like there is something that I am missing that is going to me make me feel reaaallly stupid when I figure it out :\
UPDATE 1:
I have determined that the BindingExpression.Worker.CanUpdate is false. By looking at the source code of BindingExpression I can see that that would cause it to fail. Now to figure out why that is false....
UPDATE 2:
With all the reading i have been doing i am starting to suspect a timing issue. read this
I will be able to try it when i get home
UPDATE 3:
Still working on this. Here is a bit of code. I have simplified to get minimal code.
Here is the data objects.
public abstract class Dog
{
public Dog(SexType sex)
{
NickName = string.Empty;
}
public string NickName { get; set; }
}
public class Stud : Dog
{
public Stud()
: base(SexType.Male)
{
}
}
This code works:
//code behind
public partial class AddDogWindow : Window
{
public Dog NewDog { get; set; }
public AddDogWindow()
{
InitializeComponent();
//set the DataContext here and the data object will be updated as expected...
NewDog = new Stud();
DataContext = NewDog;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
}
And the XAML
<Window x:Class="PuppyMan.AddDogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PuppyMan"
mc:Ignorable="d"
Title="AddStudDogWindow" Height="300" Width="300">
<StackPanel>
<TextBox HorizontalAlignment="Stretch" Text="{Binding NickName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Button x:Name="buttonSave" Click="buttonSave_Click">Save</Button>
</StackPanel>
</Window>
THIS is the code that BREAKS.
public partial class AddDogWindow : Window
{
public Dog NewDog { get; set; }
public AddDogWindow()
{
InitializeComponent();
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
//Notice all I have changed is moving the set DataContext out of the ctor and into this handler.
//Now the data object no longer gets updated.
NewDog = new Stud();
DataContext = NewDog;
DialogResult = true;
}
}
The idea is that I let them fully fill out the dog info and then I only create the Stud or Dame dog object based on the final decision of whether the dog is male or female. A kind of lazy data binding I suppose. I know this isn't the best design for this situation but I want to learn how to do it this way for learning purposes. Or learn why this method won't work, either is fine, I just want to learn this data binding stuff and well!
FINAL UPDATE
This post seems to be essentially the same as my problem. I am using OneWayToSource and it always sets my NickName property to the default value (in this case ""). The problem is NOT that the NickName getter is called after the setter, but that it gets set to "". The UI keeps the original value until I type in the TextBox again and INotifyPropertyChange fires then everything syncs up again.
Seems an awful lot like a bug to me that OneWayToSource would push a "" instead of the current value of the target. But I very well may be miss understanding.
Design your form such that you know beforehand which object you will get.
This can probably be done for example by checking if uses fills a particular field, or if he selects some combobox value in your form. In appropriate event of that control you can set your datacontext. And by the way Triggers are most suited here. Set trigger for your object-deciding-form-field. More can be done if you post something more, or upload some source code at dropbox.com.
//////////
You have set Bindings beforehand, and then you are changing the DataContext. Changing the DataContext will not make your TextBox value to reach the NickName property.
Secondly, you have set UpdateSourceTrigger to PropertyChanged. So, after changing your DataContext you have to make this property appear changed.
One way to do this is to :
DataContext = NewDog;
/* this will trigger property changed */
txtDogNickName.Text = txtDogNickName.Text;
string newNickName = NewDog.NickName; // updated value appear here
But this is not the proper way to do. Change your DataContext sepearately, and then edit values in your form. Changes will propagate correctly. Don't do everything in same handler. For example, you might show this form in some Button1's click handler and set DataContext, then use another Button2's click handler to get form values.
My problem turned out to be the same issue as this post. I am using OneWayToSource and when I change the DataContext my property is set to a default value which is currently "" instead of the value of the target as I was expecting. I have found no work around. I will design such that I know what object to create in advance so that the DataContext will be set in the constructor.
I have a user control written in C# & WPF using the MVVM pattern.
All I want to do is have a property in the bound ViewModel exposed to outside of the control. I want to be able to bind to it and I want any changes to the property to be picked up by anything outside the control that is bound to the exposed value.
This sounds simple, but its making me pull out my hair (and there is not much of that left).
I have a dependency property in the user control. The ViewModel has the property implementing the INotifyPropertyChanged interface and is calling the PropertyChanged event correctly.
Some questions:
1) How do I pick up the changes to the ViewModel Property and tie it to the Dependency Property without breaking the MVVM separation? So far the only way I've managed to do this is to assign the ViewModels PropertyChanged Event in the Controls code behind, which is definitely not MVVM.
2) Using the above fudge, I can get the Dependency property to kick off its PropertyChangedCallback, but anything bound to it outside the control does not pick up the change.
There has to be a simple way to do all of this. Note that I've not posted any code here - I'm hoping not to influence the answers with my existing code. Also, you'd probably all laugh at it anyway...
Rob
OK, to clarify - code examples:
usercontrol code behind:
public static DependencyProperty NewRepositoryRunProperty = DependencyProperty.Register("NewRepositoryRun", typeof(int?), typeof(GroupTree),
new FrameworkPropertyMetadata( null, new PropertyChangedCallback(OnNewRepositoryRunChanged)));
public int? NewRepositoryRun
{
get { return (int?)GetValue(NewRepositoryRunProperty); }
set
{
SetValue(NewRepositoryRunProperty, value);
}
}
private static void OnNewRepositoryRunChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != e.NewValue)
{
}
}
public GroupTree()
{
InitializeComponent();
GroupTreeVM vm = new GroupTreeVM();
this.DataContext = vm;
}
Viewmodel (GroupTreeVM.cs)
private int? _NewRepositoryRun;
public int? NewRepositoryRun
{
get
{
return _NewRepositoryRun;
}
set
{
_NewRepositoryRun = value;
NotifyPropertyChanged();
}
}
And now for my weekly "don't do that" answer...
Creating a ViewModel for your UserControl is a code smell.
You're experiencing this issue because of that smell, and it should be an indication that you're doing something wrong.
The solution is to ditch the VM built for the UserControl. If it contains business logic, it should be moved to an appropriate location in another ViewModel.
You should think of a UserControl as nothing more than a more complex control. Does the TextBox have its own ViewModel? No. You bind your VM's property to the Text property of the control, and the control shows your text in its UI.
Think of UserControls in MVVM like this--For each model, you have a UserControl, and it is designed to present the data in that model to the user. You can use it anywhere you want to show the user that model. Does it need a button? Expose an ICommand property on your UserControl and let your business logic bind to it. Does your business logic need to know something going on inside? Add a routed event.
Normally, in WPF, if you find yourself asking why it hurts to do something, it's because you shouldn't do it.
Perhaps I've misunderstood, but it seems like you're trying to use binding in the code behind?
public MyUserControl()
{
InitializeComponent();
// Set your datacontext.
var binding = new Binding("SomeVMProperty");
binding.Source = this.DataContext;
SetBinding(MyDependencyProperty, binding);
}
I'm new to WPF and am trying to create an interface that builds itself from a XML file. The newly built interface contains different controls like textboxes, labels or buttons. And the last one gave me a headache for days, because every button shall get its own command.
Example XML Code for buttons:
ID="1001" GroupID="1" Control="Button" Content="Apply" Margin="2" Width="60"
And this is the method that defines each button:
public Button Button(XElement xElement, Button newButton)
{
if (xElement.Attribute("Width") != null)
newButton.Width = Convert.ToDouble((xElement.Attribute("Width").Value));
if (xElement.Attribute("Content") != null)
{
string sContent = xElement.Attribute("Content").Value;
//Here gets the button its command
Commands Commands = new Commands();
Commands.setCommand(sContent, newButton);
newButton.Content = sContent;
}
return newButton;
}
The Content Attribute names the button and the function at the same time.
Problem: Basically I am trying to build a commands class that contains all possible commands. Each time a button is created the setCommand(sContent, newButton) method scans the commands class and sets up the matching command to this button via a Switch-Case statement.
And now each time the button is clicked the assigned command should fire.
But is it even possible to do it this way, or am I on the wrong track? Is there an easier solution to do this or am I just missing essentials of command binding?
Any hint is appreciated.
First of all, if you have your own commands defined that you want to bind to Buttons on your UI, then they should be exposed via public properties in your VM or DataContext of your UI.
Then once you have this arrangement in place, you can set the Command on button like below:
Binding binding = new Binding();
binding.Path = new PropertyPath("MyCommand"); //Name of the property in Datacontext
button.SetBinding(Button.CommandProperty, binding);
I'm using a custom list adapter for a list view. I have defined a button in the list view and the click event works, but the problem is that once the list is scrolled, it binds multiple views with the same button. So on the click of the button, the event associated with each of the associated views is fired.
How do I deal with this?
I would guess that you are misunderstanding how the list works - especially how convertView's are used.
ListViews in Android virtualise the UI - just like ListBoxes do in WP and just like UITableViews do in iOS
What this means is that if the underlying list has 1000 items, but the screen only has room for 10 items, then the list will just create 10 'containers' to show list items, and will use those containers to display just the content that is in view at the time.
The way it does this is through the Adapter - and in particular through the GetView callback - which takes a convertView as one of its parameters.
If you choose to create a new view in your GetView implementation, then you can subscribe to new events in the callback...
If instead you choose to use the convertView in your GetView implementation, then you should not subscribe to new events in the callback - not without unsubscribing the old events first.
e.g. I'm guessing your code does something like this pseudo currently:
public View GetView(int pos, View convertView)
{
TextView toShow = convertView as TextView;
if (toShow == null)
{
toShow = new TextView();
}
toShow.Text = "Item at position " + i;
toShow.Click += (s,e) => {
// do something
};
return toShow;
}
The problem with the code is that you will subscribe to Click too often... you'd need to solve it with something like:
public View GetView(int pos, View convertView)
{
TextView toShow = convertView as TextView;
if (toShow == null)
{
toShow = new TextView();
toShow.Click += (s,e) => {
// do something with the position embedded in toShow.Tag
};
}
toShow.Text = "Item at position " + i;
toShow.Tag = new WrappedPosition(i);
return toShow;
}
That's my guess anyways :)
Stuart is completely right - problem is that views in ListView are reused (to avoid creating different objects), and as different parts of list are visible, for a new position you could get any view that is not used anymore. So your code should handle this properly.
I would like to add that Garbage Collection for Java objects in monodroid works not good. In my experience, creating lots of objects derived from Java.Lang.Object will crash the application. So:
Creating new View for each new row will soon crash the applicaion, so you have to reuse convertView whenever possible.
Tag has type Java.Lang.Object, so WrappedPosition should derive from Java object. This means that rather than creating new instance every time, you should reuse same instance.
If you move click handler to a separate method, you can just unsubscribe before subscribing, so you will not need any logic "if view is null".
If you find it useful, I may post here example of code that explains how it works. Dodn't post it initially as it's quite big :)
I'm new to both C# and WPF. I've written a simple program. I have a class called Counter, that exposes a read-only property Count that starts out at 0, and a public method Increment that simply increments the count by one. Counter implements INotifyPropertyChanged.
I have a Window class (code is below). I pass an instance of a Counter object to the constructor and perform a binding. The window has a button and a label. The label is bound to the counter's Count property, and the button calls Increment.
This all works.
However, most examples I've seen around the net and MSDN mostly deal with defining the binding in XAML. How can I modify my example here to move the binding operation out of code behind and into the markup? The Binding property in the Properties window of VS2010 doesn't seem to know how to do what I want. Perhaps it's not possible?
One additional question: I don't think this example fits MVVM... My Counter class stands alone, is not tied to a view anywhere except through its property. However, the CounterWindow class is holding a reference to it. Is this the proper location for this reference? I also though that perhaps I should be creating the window, then setting a property (e.g. CounterObject) that I would use instead of passing through via the constructor.
public partial class CounterWindow : Window {
Counter ctr;
public CounterWindow(Counter ctr) {
InitializeComponent();
this.ctr = ctr;
Binding b = new Binding("Count");
b.Source = ctr;
CounterLabel.SetBinding(Label.ContentProperty, b);
}
private void IncrementButton_Click(object sender, RoutedEventArgs e) {
ctr.Increment();
}
}
Something like this:
public CounterWindow(Counter ctr)
{
InitializeComponent();
DataContext = ctr;
}
Markup:
<Label Content="{Binding Count}" />
UPD.
There's two common approaches in MVVM: view-first and model-first.
View first means that you initially create the view, and then view creates view model, which it is bound to.
Model-first means that first you create the view model, then view model creates its view and passes itself (via constructor or via DataContext property setter) as data context of the view.
Hope this helps you.