I have an interface IScreenViewModel, and to simplify the problem I can
RedScreenViewModel : IScreenViewModel
GreenScreenViewModel : IScreenViewModel
etc..
This means I have a RedScreenView.xaml which makes a RedScreenViewModel instance, likewise for all subsequent color screens.
IScreenViewModel has some properties that you must implement e.g.
interface IScreenViewModel
{
public Color ScreenColor{get;set;}
}
I have a ViewmodelWrapper class which holds all viewmodels instances. ScreenViewModels, MenuViewModels etc...
Because I am using DevExpress I can't bind the DataContext directly in the Main.xaml.cs file for reasons I still don't know yet.
So in main for example.
I can't have
ScreenLabel.DataContext = viewModelWrapper.ScreenViewModel
I would have to do in main:
DataContext = viewModelWrapper;
This way the parent Window can see all child elements.
In a RedScreenView.xaml I can have something like this:
<Label Background="ScreenViewModel.ScreenColor"/>
And hopefully the data binding should look in the ViewModelWrapper find the IScreenViewModel.ScreenViewModel object and use the correct ScreenColor object using dynamic binding/polymorphism.
There are cases where a screen can have more properties, so let say in
GreenScreenViewModel along with ScreenColor property inherited, it can have its own property maybe DifferentProperty.
The problem is: I have a Factory that returns a screen object depending on what screen the user wants. It returns the correct screen object, but when it notifies the View to update itself it looks at the new object but using the wrong XAML. If that makes any sense.
I do something like this in a ViewModelWrapper method.
MainGui.ScreenWrapper.LayoutRoot.Clear() ;
MainGui.ScreenWrapper.Items.Clear() ;
MainGui.ScreenWrapper.LayoutRoot.Add(screenFactory.GetSelectedScreen("RedScreen").GetLayoutRoot()
MainGui.UpdateLayout() ;
ScreenViewModel = screenFactory.GetSelectedScreen("RedScreen").GetViewModel() ;
Ignore the fact that I called factory twice...
ScreenWrapper is LayoutGroup that holds the screens. When I swap the Views (screens) using that code I am hoping that it would use the correct bindings.
So let's say I swap from GreenScreenViewModel to RedScreenViewModel, remember GreenScreenViewModel one more property than RedScreenViewModel and in the GreenScreenView I had something like this:
<Label Content="ScreenViewModel.DifferentProperty"/>
When the swap is done and ScreenViewModel notifies that is now pointing to RedScreenViewModel it throws an exception. I am strongly assuming this is because Layout isn't being refreshed and it is still using the wrong view.
The output error in debug mode is
"Cannot find property DifferentProperty in viewModelWrapper.ScreenModel"
Which isn't right because I have already deleted THAT GreenScreenView, I updated the layout, I know there is a LayoutChanged event or something like that, so that could have been raised as well so why is it still seeing the wrong View?
How can I update ScreenWrapper.LayoutRoot to "see" the new View with a different binding code.
Heavens, I hope that was clear.
EDIT: At Michael thanks for replying. Yes there is an actual exception - "NullReferenceException" in the thirdparty dll I am using. And that is because it can't find the property. I am sure I didn't make myself clear but maybe the question should be: When deleting and inserting usercontrols from a visual tree -how can I refresh the visual tree to see new bindings? If I can refresh the visual tree it should solve my problem. UpdateLayout() doesn't work
EDIT:
At Michael thanks for replying. Yes there is an actual exception - "NullReferenceException" in the thirdparty dll I am using. And that is because it can't find the property. It throws the exception when I call OnPropertyChanged, and yeah the handler isn't null!
I am sure I didn't make myself clear but maybe the question should be:
When deleting and inserting usercontrols from a visual tree -how can I refresh the visual tree to see new bindings? If I can refresh the visual tree it should solve my problem.
UpdateLayout() doesn't work.
Firstly, you say
Because I am using DevExpress I can't bind the DataContext directly in the Main.xaml.cs file for reasons I still don't know yet.
Express should not be the problem here. You need something to bind to that will return the appropriate ViewModel. Have a look here for discussion on this subject.
Secondly, you say the error is
Cannot find property DifferentProperty in viewModelWrapper.ScreenModel
This is not necessarily a problem, and does not cause an exception. When you change bindings dynamically, INotifyPropertyChanged events fly off all over the place and there may be a period of 'uncertainty'. I am assuming your ViewModels implement INotifyPropertyChanged.
I think the key is probably looking closer at the exception, if there is one (because "Cannot find property" is a debug message, not an exception). To get some clarity you might want to turn off the binding message as described here. If there is an actual exception, please edit with the details.
Related
I have a WPF window that takes a few parameters in it's constructor. I then use these constructors to setup the state of the window. Part of that constructor process is instantiating my view model class that is then set as the windows DataContext.
My question is when should I set my DataContext equal to my view model object-- before or after the call to InitializeComponent()?
I ask because if I set it beforehand, I then need to manually launch code that is executed after the window has been initialized, because certain events should fire when the DataContext is assigned, or re-assigned.
It is my assumption that there shouldn't be any binding issues if I set the DataContext after the call to InitializeComponent() but I wanted to ask for advice on the matter before making the final call to wire up my window this way. Could I be missing something the could come back to haunt me if I set my DataContext after the call to InitializeComponent()?
My question is when should I set my DataContext equal to my view model object-- before or after the call to InitializeComponent()?
It shouldn't matter unless you rely on some bindings that are established during the call to the InitializeComponent(), like ElementName bindings:
Cannot bind ItemsSource to ElementName
The InitializeComponent() method itself locates a URI to the compiled XAML file and passes it to a LoadComponent() method that parses the BAML, i.e. the compiled XAML, and creates instances of the elements that you have defined in your XAML markup:
What is the connection between .xaml and .xaml.cs files
Simply setting the DataContext property of the window to an instance of a view model class that elements in the view bind to might as well be done after the call to the InitializeComponent() method. These bindings aren't resolved before the constructor returns anyway.
Here's my addition to #mm8's answer:
Usually it does not matter, but set DataContext after InitializeComponents. When DataContextChanged event is called, you naturally expect, that components are already initialized.
Also it's good to know whether the components can be initialized without DataContext and separate possible initialization issues from binding issues. If you set DataContext before InitializeComponents, the binding issues may result in an exception in InitializeComponents.
Make you ViewModel constructor very fast. Don't do any DB calls or any I/O calls, etc. You want to display the UI as soon as possible.
Make sure your ViewModel constructor never throws an exception. Parameter validations is OK, but just for debugging purposes. It should never happen in production.
If you need to load data into viewmodel, create separate async method called e.g. Activate(), which you will call from View's Loaded or OnNavigatedTo event.
Additionally, if you subscribe to some events in ViewModel, you should unsubscribe. Ideal place for subscription is Activate method, resp Deactivate to unsubscribe. If you subscribe in ViewModel's ctor, it may happen that Activate/Deactivate will never be called and you introduced memory leak.
If you feel your bindings are slowing the UI, try to use {Binding IsAsync=True}, resp x:Bind, or try to use codebehind to set the properties in worst case.
Different from what you ask, I suggest two changes:
Set the DataContext of an inner element and not on the Window / UserControl itself.
Set the DataContext on Loaded instead of the constructor.
These points are more obvious when looking at a UserControl, which will probably be embedded at multiple points, but remember that a Window can be created by explicit startup code instead of some App.StartupUri.
Regarding the first point, consider the OOP design basics. Forget about WPF / XAML specifics and remember that you derive from a Window class and create a subclass of it. The contract of this class includes a public get/set property named DataContext which accepts any kind of object. So you should at least think about, how bad you will screw up, if someone is replacing your DataContext from the outside. When you instead set the DataContext on the next-inner FrameworkElement inside the window, it is hosted in an environment that is owned by the window.
Setting the DataContext on Loaded is working for me, while I ran into problems with constructor time setting. However, I can't actually recall the details of it, maybe it was related to the visual designer (that I'm not using anymore). For other controls it is easier to explain: constructor time initialization sucks when hosted in a virtualizing panel, also property initializers (new MyControl { Prop = Value }, XAML property assignments, ...) are not handled by time the constructor runs, so objects tend to be in a different state than how they are presented later.
First of all, let me apologize for the super-noob question about WPF and binding. I have started, a few days ago, to get interested in WPF and its implementation with XAML markup and C# code-behind in Visual Studio Express 2013, and I'm trying to bind the contents of a button to a property that is part of a singleton class. And I can't, apparently.
What I want to do is this:
I have a button saying "Start" and I want, on click, to have the button contents change to a timer + stop, something like "00:00:02 -- Stop" and, on click again, to have it change back to "Start".
I have this class that I have designed as a singleton to prevent it from being instantiated more than once, which contains an instance stopwatch of System.Diagnostics.Stopwatch, and a string property which I change either to "Start" or stopwatch.Elapsed.TotalSeconds.ToString () + "Stop" back and forth.
The problem is that when I try to refer to my singleton class in the XAML markup, like so (to be honest, I don't really know what I'm doing, I'm just inspiring myself from diverse examples I've found over the web):
<Window
...
xmlns:local="clr-namespace:myNameSpace">
<Grid>
<local:mySingleton x:Key="myStringProperty" />
...
</Grid>
</Window>
I get a slew of compiler errors (6 total) of which some say: "The type mySingleton does not include any accessible constructors." and "The "Key" attribute can only be used on an element that is contained in "IDictionary"."
I'm clueless as to what to do, knowing that I don't want to make the constructor public (and therefore get rid of the singleton thing).
Any pointers towards the right direction ?
Thank you guys for your help.
At first you have problem in displaying your string property. You have to use text box and then use property for text binding:
<TextBox Text="{Binding Path=myStringProperty}"/>
Also you have to set your class (view model) to the Window.DataContext and your property have to call OnPropertyChanged event (see: WPF: simple TextBox data binding).
Now about singleton. The singleton shouldn't be used for this. You should have window (view) and that window have to work with some class (view model, you have one instance per one window) and if you want to run more windows together and you always want same result, then inside that view model you should have some static object (it can be that timer, or some class which will handle requests about get result from timer which will inside that class what can be singleton). So it could looks like Window (view) -> view model class -> static timer / class which will works with timer inside itself.
Is there a way to manipulate to order in which Dependency Properties receive their values :
XAML :
<Window local:MyHelper.SomeAttachedProp="{Binding Something , Mode=OneWay}">
<Grid>
<TextBlock Text="Let him go first"
local:MyHelper.AnotherAttachedBooleanProp="True" />
</Grid>
</Window>
CS :
public static class MyHelper
{
propa ....
OnSomeAttachedPropChanged( .... )
{
// I WANT TO GET HERE FIRST
}
propa .....
OnAnotherAttachedBooleanPropChanged( .... )
{
// I WANT TO GET HERE SECOND
}
}
Currently i'm reaching AnotherAttachedBooleanPropChanged before OnSomeAttachedPropChanged
is there any way to control to order in which Dependency Properties are updated ?
Edit :
I just remembered/realized something , the DP with a direct assignment will get updated before the bound one .
I don't know of any direct way to do that. And i personally never had the desire to do so. When you only use the OnXYZChanged methods to register to events (which is imo the most common case) you could register to an event which better suits the order of your desired code execution. For example, in the OnXYZChanged register to Loaded event and handle that, the order of these should be afair from Root to child, and therefore in the correct order.
Another way would be to test which one was called second, and do your logic in there.
Another idea would be to use the Dispatcher and do all the logic at a later time where its sure that everything is loaded.
And a final idea, You could traverse with the VisualTreeHelper from the child element in the OnXYZChanged to the underlying window and set the property on the window first and then handle the logic for the child.
#Your edit:
If you think about it, it makes sense. The visual tree must be created and everything must be chained before the Binding can traverse the tree to find the appropiate DataContext.
EDIT:
New Information, just managed to get a logger working (I honestly had no idea cm had one!) and i'm given this message when attempting to use TryClose().
TryClose requires a parent IConductor or a view with a Close method or IsOpen property
I have been stuck on this for a number of days now, and research has turned up zero, I tried posting a question previously about this issue but it received no answers so i assume I didn't word it correctly.
I have a view and viewmodel ContentView/Model which has the following code in them:
ContentView:
<MenuItem Header="New Project" x:Name="OpenProject" cal:Message.Attach="[Event Click] = [Action NewProject()]"/>
ContentViewModel:
public void NewProject()
{
NewProjectViewModel viewModel = new NewProjectViewModel(_projectManager);
_windowManager.ShowWindow(viewModel);
//If the result is true, we have a new project, otherwise they cancelled the window.
if (viewModel.Result)
{
Project newP = new Project(0, viewModel.ProjectNo, viewModel.ProjectName, 0, 0);
_projectManager.Insert(newP);
}
}
and the viewmodel NewProjectViewModel has the following:
public void Create()
{
this.Result = true;
TryClose(true);
}
which is called in the same was as previously using an message.attach on the OK button of the dialog.
However the issue is that TryClose() always fails to close the dialog, and as i don't have the source of caliburn.micro i can't debug inside TryClose() however doing (GetView() As Window).Close() also fails because GetView() always returns null.
I'm at a complete loss as to how i can close this dialog, so any help or suggestions would be greatly appreciated.
EDIT:
Since i seem to be getting no answers on this, where as previous questions do, I'll assume i have information missing. In an attempt to understand the issue i think it may have something to with using the view first approach.
In the NewProjectView i have the following:
xmlns:cal="http://www.caliburnproject.org"
cal:Bind.Model="ShippingClient.ViewModels.NewProjectViewModel"
This is used to bind the viewmodel rather than the automatic way that is usually used, perhaps this is why GetView() returns null?
You are going to absolutely kick yourself:
Remove the cal:Bind.Model and cal:View.Model bindings...
If you are working ViewModel-First (i.e. you are creating a viewmodel and showing it using WindowManager or in a conductor) all the binding stuff that glues the viewmodel to the view is done for you by CM.
In this case you shouldn't use any View-First bindings. What you are essentially doing is newing up another instance of your VM and binding that to your view... so you have two viewmodels in the background, one wired up nicely but not bound any more, and a non-wired up instance which is bound to your view but doesn't know about the conductor.
Just remove any bindings to the viewmodel in your view and it will all work!
Suppose I have a modal dialog with a textbox and OK/Cancel buttons. And it is built on MVVM - i.e. it has a ViewModel object with a string property that the textbox is bound to.
Say, I enter some text in the textbox and then grab my mouse and click "OK". Everything works fine: at the moment of click, the textbox loses focus, which causes the binding engine to update the ViewModel's property. I get my data, everybody's happy.
Now suppose I don't use my mouse. Instead, I just hit Enter on the keyboard. This also causes the "OK" button to "click", since it is marked as IsDefault="True". But guess what? The textbox doesn not lose focus in this case, and therefore, the binding engine remains innocently ignorant, and I don't get my data. Dang!
Another variation of the same scenario: suppose I have a data entry form right in the main window, enter some data into it, and then hit Ctrl+S for "Save". Guess what? My latest entry doesn't get saved!
This may be somewhat remedied by using UpdateSourceTrigger=PropertyChanged, but that is not always possible.
One obvious case would be the use of StringFormat with binding - the text keeps jumping back into "formatted" state as I'm trying to enter it.
And another case, which I have encountered myself, is when I have some time-consuming processing in the viewmodel's property setter, and I only want to perform it when the user is "done" entering text.
This seems like an eternal problem: I remember trying to solve it systematically from ages ago, ever since I've started working with interactive interfaces, but I've never quite succeeded. In the past, I always ended up using some sort of hacks - like, say, adding an "EnsureDataSaved" method to every "presenter" (as in "MVP") and calling it at "critical" points, or something like that...
But with all the cool technologies, as well as empty hype, of WPF, I expected they'd come up with some good solution.
At critical points, you can force the binding to push through to your view model:
var textBox = Keyboard.FocusedElement as TextBox;
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
Edit:
OK, since you don't want hacks we have to face the ugly truth:
In order to implement a clean view, the properties exposed by your view model should be friendly to frequent binding updates.
An analogy we can use is a text editor. If the application was a giant text box bound to a file on disk, every keystroke would result in writing the whole file. Even the concept of saving is not needed. That's perversely correct but terribly inefficient. We all immediately see that the view model needs to expose a buffer for the view to bind to and this re-introduces the concept of save and forces state handling in our view model.
Yet, we see this is still not efficient enough. For even medium-sized files the overhead of updating the whole-file buffer on every keystroke becomes unbearable. Next we expose commands in our view model to efficiently manipulate the buffer, never actually exchanging the whole buffer with the view.
So we conclude that in order to achieve efficiency and responsiveness with pure MVVM, we need to expose an efficient view model. That means that all text boxes can be bound through to properties with no ill effects. But, it also means that you have to push state down into the view model to handle that. And that's OK because a view model is not the model; it's job is it to handle the needs of the view.
It's true that we can rapidly prototype user interfaces by taking advantage of shortcuts like binding on focus changes. But binding on focus changes can have negative consequences in real applications and if so then we should simply not use it.
What is the alternative? Expose a property friendly to frequent updates. Call it the same thing as the old inefficient property was called. Implement your fast property using the slow property with logic that depends on the state of the view model. The view model gets the save command. It knows whether the fast property has been pushed through to the slow property. It can decide if when and where the slow property will be synched to the model.
But you say, haven't we just moved the hack from the view to the view model? No, we have lost some elegance and simplicity, but go back to the text editor analogy. We have to solve the problem, and it is the view model's job to solve it.
If we want to use pure MVVM and we want efficiency and responsiveness, then lame heuristics like let's avoid updating the binding source until the element loses focus won't help. They introduce as many problems as they solve. In that case, we should let the view model do its job, even if means adding complexity.
Assuming we accept it, how can we manage the complexity? We can implement a generic wrapper utility class to buffer the slow property and allow the view model to hook its get and set methods. Our utility class can auto-register for save command events to reduce the amount of boilerplate code in our view model.
If we do it right, then all the parts of the view model that were fast enough to be used with property changed binding will all still be the same, and the others that were worthy of asking the question "Is this property too slow?" will have a small amount of code to address the issue, and the view is none the wiser.
This is a tricky one and I agree a non-hack and more-or-less code free solution should be found. Here are my thoughts:
The view is responsible because it sets IsDefault to true and allows for this 'problem'
The ViewModel should not be responsible in any way to fix this it might introduce dependencies from VM to V and thus breaking the pattern.
Without adding (C#) code to the View all you can do is either change bindings (e.g. to UpdateSourceTrigger=PropertyChanged) or add code to a base class of the Button. In the base class of the button you might be able to shift focus to the button before executing the command. Still hack-ish but cleaner than adding code to the VM.
So at the moment the only 'nice' solutions I see require the view developers to stick to a rule; set the binding in a specific way or use a special button.
I would add a Click event handler for the default button. The button's event handler is executed prior the command will be called, so the data bindings can be updated by changing the focus in the event handler.
private void Button_Click(object sender, RoutedEventArgs e) {
((Control)sender).Focus();
}
However, I don't know if similar approach can be used with other shorcut keys.
Yes, I have quite some experience. WPF and Silverlight still have their pain areas. MVVM doesn't solve it all; it is not a magic bullet and the support in the frameworks is getting better but still lacks. E.g., I still find editing deep child collections a problem.
At the moment I handle these situations case by case because a lot depends on the way the individual view have to work. This is what I spend most of my time on because I generate a lot of plumbing using T4 so I have time left for these quirks.
The problem is that the TextBox's text has a default source trigger of LostFocus instead of PropertyChanged. IMHO this was a wrong default choice since it is quite unexpected and can cause all sorts of problems (such as the ones you describe).
The simplest solution would be to always explicitly use UpdateSourceTrigger=PropertyChanged (as the others suggested).
If this isn't feasible (for whatever reason), I would handle the Unloaded, Closing or Closed events and manually update the binding (as shown by Rick).
Unfortunately it seems that certain scenarios are still a bit problematic with a TextBox, so some workarounds are necessary. For example, see my question. You might want to open a Connect bug (or two) with your specific problems.
EDIT:
Pressing Ctrl+S with focus on the TextBox, I would say the behavior is correct. After all, you are executing a command. This has nothing to do with the current (keyboard) focus. The command may even depend on the focused element! You are not clicking on a button or similar, which would cause the focus to change (however, depending on the button, it may fire the same command as before).
So if you want to only update the bound Text when you lose focus from the TextBox, but at the same time you want to fire a command with the newest contents of TextBox (i.e. the changes without it having lost focus), this does not match up. So either you have to change your binding to PropertyChanged, or manually update the binding.
EDIT #2:
As for your two cases why you cannot always use PropertyChanged:
What precisely are you doing with StringFormat? In all my UI work so far I use StringFormat to reformat data I am getting from the ViewModel. However, I am not sure how using StringFormat with data which is then again edited by the user should work. I am guessing you want to format the text for display, and then "unformat" the text the user enters for further processing in your ViewModel. From your description, it seems it isn't "unformatted" correctly all the time.
Open a Connect bug where it isn't working as it should.
Write your own ValueConverter which you use in the binding.
Have a separate property with the last "valid" value and use that value in your ViewModel; only update it once you get another "valid" value from the property you use in databinding.
If you have a long-running property setter (ex. a "validate" step), I would do the long-running part in a separate method (getters and setters should normally be relatively "fast"). Then run that method in a worker thread/threadpool/BackgroundWorker (make it abortable so you can restart it with a new value once the user enters more data) or similar.
What you think about proxy command and KeyBinding to ENTER key?
EDITED:
There we have one utility command (like converter), which requires knowledge about concrete view. This command can be reused for any dialog with same bug. And you add this functionality/hack only in view where this bug exists, and VM will be clear.
VM creates to adapt business to view and must provide some specific functionality like data conversion, UI commands, additional/helper fields, notifications and hacks/workarounds. And if we have leaks between levels in MVVM we have problems with: high connectivity, code reuse, unit testing for VM, pain code.
Usage in xaml (no IsDefault on Button):
<Window.Resources>
<model:ButtonProxyCommand x:Key="proxyCommand"/>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding Source={StaticResource proxyCommand}, Path=Instance}"
CommandParameter="{Binding ElementName=_okBtn}"/>
</Window.InputBindings>
<StackPanel>
<TextBox>
<TextBox.Text>
<Binding Path="Text"></Binding>
</TextBox.Text>
</TextBox>
<Button Name="_okBtn" Command="{Binding Command}">Ok</Button>
</StackPanel>
There used special proxy command, which receive element (CommandParameter) to move focus and execute. But this class requires ButtonBase for CommandParameter:
public class ButtonProxyCommand : ICommand
{
public bool CanExecute(object parameter)
{
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return false;
return btn.Command.CanExecute(btn.CommandParameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (parameter == null)
return;
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return;
Action a = () => btn.Focus();
var op = Dispatcher.CurrentDispatcher.BeginInvoke(a);
op.Wait();
btn.Command.Execute(btn.CommandParameter);
}
private static ButtonProxyCommand _instance = null;
public static ButtonProxyCommand Instance
{
get
{
if (_instance == null)
_instance = new ButtonProxyCommand();
return _instance;
}
}
}
This is just idea, not complete solution.