Binding singleton property to XAML markup - c#

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.

Related

If setting a DataContext within a constructor, does it matter if I set it before or after the call to InitializeComponent()?

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.

Calling Usercontrol method from another form

I have this Usercontrol with a Listview loaded in the Mainwindow:
<Controls:MetroAnimatedSingleRowTabControl Grid.Row="1" x:Name="MainTabControl" Controls:TabControlHelper.IsUnderlined="True" Margin="10,0,0,1">
<TabItem Controls:ControlsHelper.HeaderFontSize="40" Header=" List" Foreground="#CCB5BABB" Controls:ControlsHelper.HeaderFontStretch="UltraExpanded" HorizontalAlignment="Left" VerticalAlignment="Top" >
<load:Usercontrol1 DataContext="{Binding}" />
</TabItem>
From this Usercontrol a ButtonClick calls another form for entering new data. After saving the data to database, I call a method loading the list in Usercontrol by referencing the entire Usercontrol to the entry window :
private readonly Usercontrol1 temp;
public newDataEntry(Usercontrol1 temp2)
{
InitializeComponent();
temp= temp2;
}
private void buttonentry(object sender, RoutedEventArgs e)
{
temp.fillList(); // list in Usercontrol fill
this.Close();
}
Since I want to use the same entry form with different Usercontrols, is there a more effective way to call method in Usercontrol?
Without a good Minimal, Complete, and Verifiable example that shows clearly what you are doing, why you want to call this method, what the method does, and what specific problem you are having generalizing the action, it is impossible to know for sure what the best answer for your scenario is. That said, some discussion can be provided.
First and foremost, it is a mistake for your newDataEntry class to depend on the Usercontrol1 class at all. This should already be apparent, due to the issue you are running into trying to reuse it with other UserControl classes, but it is also a basic OOP concept: a class that exists to support some other class should not itself carry a dependency on that other class. Doing so breaks reusability in a way that is fundamentally opposite a primary goal of OOP.
So how do you get rid of this dependency? Well, the most general way in C# would be for your Usercontrol1 to subscribe to the newDataEntry object's Closed event. Then it can do whatever it wants at that time, including calling its own fillList() event.
Of course, if the newDataEnty window is used modally (i.e. you call ShowDialog()), then subscribing to the Closed event is overkill. You can just call whatever code you need to when the ShowDialog() method returns.
All that said, the name fillList() hints that you're copying list data directly into some list-based control (e.g. the ListView you mentioned). When in fact, in a WPF program, you should be manipulating only view models and letting the UI respond accordingly. Again, without a good MCVE showing context, it's impossible to say for sure that's what you're doing, never mind provide any specific advice along those lines. Suffice to say, it's likely that this code doesn't belong in the Usercontrol1 class at all.
See also these related posts:
How to call method of the main WPF window from the modal window? - this seems most applicable. Unfortunately, the accepted and top-voted answer is one of the worst (introduces exactly the kind of class coupling you're trying to avoid here), but there are other answers with some useful information.
WPF MVVM call ViewModel Save method on Window Close - this discusses doing something similar in the context of using a proper view model. May or may not be directly applicable to your scenario.
Communicate between two windows forms in C# - this is about Winforms, but in this particular scenario the basic techniques are similar. In this particular case, you wouldn't need to declare a new event, because the Closed event already seems to do what you want.

Opening a second Window from MainWindow following MVVM and loose coupling

At first: This App and Question is for learning purpose
I'm on a new application and facing the problem that I want to open a Window when the user clicks on a Button in the MainView. In the past I'd have designed a Command which just creates the new Window and displays it
new RelayCommand((x)=>new SecondWindow().Show());
Now with this new Project I'm trying to fully decouple all classes from each other. To achieve this my App consists of 4 Assemblies (BL, COM, DAL and UI).
As in each WPF Application, the App starts with the MainWindow.xaml. The MainWindow.cs will create it's instance of MainWindowViewModel:
public ViewModel VM {get; private set;}
public class MainWindow(){
VM = new ViewModel();
InitializeComponent();
}
(which already violates loose coupling) (Any tips on how to make it better?)
My last attempt is to create an instance of my second Window inside my main window
<Window.Resources>
<local:SecondWindow x:Key="sw"/>
</Window.Resources>
and pass it as a CommandParameter to my Command
CommandParameter="{StaticResource sw}"/>
new RelayCommand((x)=> ((Window)x).Show());
This solution works but has one big disadvantage - the second window get's created immediately after the app starts - and so does it's ViewModel which starts some heavy processes (DB Connections etc.)
I've heard something abour IoC principle but I really don't know how to use it with an wpf application.
You are thinking along the right lines.... you basically have to create a List of ViewModels as your application starts up, then you can switch between them as the user presses buttons and pass the name of the ViewModel as a CommandParameter to your Command handler....
You might find this link to Rachel Lim's Blog
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
Also, I'm not going to post any code here coz it simply gets too complicated. So here is a download to just about the simplest example I could come up with
http://www.mediafire.com/download/3bubiq7s6xw7i73/Navigation1.rar
Download and un-RAR it (with win RAR) You will need to step though the code, figure out what its doing and how its doing it then modify it to suit your needs... Or modify your needs to suit the code.....
The example is a modification of Rachel Lim example. It simply contains Views and ViewModels, there are no Models or data. It demonstrates switching between two different Views.
UPDATE 1
With specific reference to the demo code.... Your VMs are added to a static collection of VMs (see AddViewModel function), each View ( the DataTemplate associates View with ViewModel) is selected when you click a button for example, by calling 'SelectViewCommand' which in turn sets Current_ViewModel to the selected ViewModel... the corrisponding ContentControl is then updated to display that currently selected View...
I know is confusing and very difficult to explain
When you press a button to 'change Views' you are actually changing the value of the property that your ContentControl is bound to, so you have to call the correct SelectViewCommand in the SAME instance of the class that your ContentControl is bound too...
In the demo you'll see that in the 'LogOn_View' I call
Command="{Binding DataContext.SelectViewCommand, ElementName=Base_V}"CommandParameter="Main_ViewModel"
Here I am calling the SelectViewCommand in the Base_ViewModel (x:Name="Base_V" in Base_View XAML), That's because I want to change the View that is displayed in the Base_View's 'ContentControl'
In Main_View I call
Command="{Binding SelectViewCommand}" CommandParameter="MainV1_ViewModel"
Here I am calling the SelectViewCommand in the Main_ViewModel, That's because I want to change the View displayed in the MainView's 'ContentControl'....
I typically create a WindowService class for managing window changes/dialogs in MVVM. Having "View" code in the ViewModel (i.e. Window.Show()) goes against MVVM principles. For example:
public class WindowService : IWindowService
{
public void ShowDialog<T>(ViewModelBase viewModel) where T : IApplicationDialog
{
IApplicationDialog dialog = (IApplicationDialog)Activator.CreateInstance(typeof(T));
dialog.Show();
}
}
And then your call from the ViewModel would look something like:
windowService.ShowDialog<SecondWindow>(new SecondWindowViewModel());
If you're using DI, you can pass a reference to the IoC container to the window service and create the window instances from that rather than using Activator.CreateInstance (i prefer the DI approach personally)

Refresh View databindings using UpdateLayout or some other alternative

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.

StaticResource works in design, throws XamlParseException at runtime

This seems so silly, but I've been looking for a couple of hours and nothing I find fixes this problem.
I'm using MvvmLight. I have a simple MainWindow that has nothing in it but a Grid. I have another class that is a WPF UserControl that contains one button. Both of these have
DataContext="{Binding Main, Source={StaticResource Locator}}"
in their node definition (either Window or UserControl). App.xaml has the standard entry in its Resources:
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
I'm playing with messaging, and I've got a message that says load this UserControl in the MainWindow. I can trace through and everything works just peachy -- right up to the point where it is instantiating the new UserControl and the code behind does its InitializeComponent().
At this point the application stops in the user control's .xaml file with the XamlParseException complaining it cannot find "Locator" (and, yes, I know, resource names are case sensitive, thank you).
This makes absolutely no sense to me. In design mode in the user control, I can navigate to the data context I want -- it sees Locator, and Locator sees Main, and Main sees the property I'm binding to the button. And the designer SHOWS the text on the button.
Anybody???
This is C# in VS 2010 Ultimate, v10.0.30319.1, running in Windows 7.
Well, it does matter about the messaging and the order of that with regard to instantiating the xaml object (e.g., InitializeComponent). In my original code, I was sending the message to load the UserControl from the MainViewModel constructor; this was occurring during the MainWindow's InitializeComponent() call.
Removed the message from the constructor and simply loaded the UserControl in the MainWindow AFTER returning from the InitializeComponent() method. Sure enough, no problem, it worked fine.
So then I added a method to MainViewModel called UIReady() and called that from MainWindow AFTER InitializeComponent() was done. The UIReady() method in MainViewModel simply issues the message to load the UserControl. And it works, no problem. So an extra method to coordinate things, but it accomplishes what I want -- the MainWindow has no idea what UserControl is going to be loaded in it until a ViewModel requests a particular View. Then it loads the UserControl that represents that View.
I don't know enough about all the ends and outs of InitializeComponent, but SOMETHING in there does not like initializing another xaml obect until it finishes its own initialization; it loses touch with things -- like Application Resources.

Categories

Resources