Modifying ViewModel Properties outside of ViewModel - Values aren't saving - c#

Is it possible to modify properties of a ViewModel by using an instance of ViewModelLocator elsewhere in the code? When I try, any value I try to assign seems to be discarded.
For example, a ViewModel, an instance of which named "Game" is contained in my ViewModelLocator. It has a string property named "Test". When I try to modify it this way:
(App.Current.Resources["Locator"] as ViewModelLocator).Game.Test = "Testing";
System.Windows.MessageBox.Show((App.Current.Resources["Locator"] as ViewModelLocator).Game.Test);
or
ViewModelLocator _viewModelLocator = new ViewModelLocator();
_viewModelLocator.Game.Test = "Testing";
System.Windows.MessageBox.Show(_viewModelLocator.Game.Test);
The messageboxes display the value of the string declared in the ViewModel itself if there is one. If a value hasn't been assigned in the ViewModel, the messageboxes show up empty. Either way, they don't display "Testing".
How can I make this work? I'm using MVVM Light with Unity.
public class ViewModelLocator
{
private static Bootstrapper _bootstrapper;
static ViewModelLocator()
{
if (_bootstrapper == null)
_bootstrapper = new Bootstrapper();
}
public GameViewModel Game
{
get { return _bootstrapper.Container.Resolve<GameViewModel>(); }
}
}
public class Bootstrapper
{
public IUnityContainer Container { get; set; }
public Bootstrapper()
{
Container = new UnityContainer();
ConfigureContainer();
}
private void ConfigureContainer()
{
Container.RegisterType<GameViewModel>();
}
}

It looks like this is a problem with Unity. I switched back to MVVM Light's SimpleIoc and it works without a hitch.

When you call Container.RegisterType<GameViewModel>(); this registers the type GameViewModel with the default lifetime manager. The default lifetime manager for the RegisterType method is the TransientLifetimeManager which means that every time Resolve is called a new instance is returned.
So every time the Game property is called a new instance of GameViewModel is returned. Any modifications to the object will only be made to that object (and lost when the object is GC'd). The next time the Game property is called a new instance of GameViewModel is returned.
So, assuming you only want one GameViewModel you should register it as a singleton:
private void ConfigureContainer()
{
Container.RegisterType<GameViewModel>(new ContainerControlledLifetimeManager());
}

Related

Changing Controls from anywhere

What I want to do:
I want do change a background color of a button from anywhere in my code (other classes Xamarin Forms). For example a button A in Page A changes the color of button B in Page B
on Windows you can use the MethodInvoker Delegat which isn't available on Android/iOS.
Can you give me a hint?
I tried it with the text of the buttons before with the MVVM approach.
in my PageB.xaml:
<Button Name="Button_B" Text="{Binding MyText}"/>
in my PageB.cs in public PageB
BindingContext = new MVVMPageB();
in my MVVMPageB.cs
private string myText;
public string MyText
{
get => mytring;
set
{
mystring = value;
PropertyChanged?
.Invoke(this, new PropertyChangedEventArgs(MyText)));
}
if i call:
MyText("Test");
in my MVVMPageB.cs it works fine. but i dont know how to access this from anywhere else.
i tried:
var Testobjekt = new MVVMPageB() //pretty sure thats not correct
Testobjekt.MyText("Test"); //wont work
Technique 1
This is a Singleton pattern for MVVMPageB.
This works if you never have two "Page B"s. IF there is a Page B on the navigation stack (so you can "Go Back" to it), and you display ANOTHER Page B, THEN this will not work well, because both Page B's will refer to the SAME MVVMPageB instance.
public class MVVMPageB : ...
{
// "Singleton": This is the only instance of MVVMPageB.
private static MVVMPageB _It;
public static MVVMPageB It
{
if (_It == null)
_It = new MVVMPageB();
return _It;
}
// Your constructor.
// It is private; only used via "It" getter above.
private MVVMPageB()
{
...
}
}
Code in another class, to access a member of the MVVMPageB.
MVVMPageB.It.MyText("Test");
Replace this code:
BindingContext = new MVVMPageB();
With this code:
BindingContext = MVVMPageB.It;
NOTE: Because MVVMPageB.It is static, if you go to Page B a second time, it will show the values you had last time (within the same app session).
Technique 2
A more robust approach, which works even if you create another Page B, requires having some way to pass the current instance of MVVMPageB to MVVMPageA or to PageA.
A complete example depends on exactly how/where you create each page. But this shows the idea.
public class MVVMPageB : ...
{
// Your constructor. Add parameters as needed.
public MVVMPageB()
{
...
}
}
public partial class PageB : ...
{
// Convenience property - our BindingContext is type MVVMPageB.
public MVVMPageB VMb => (MVVMPageB)BindingContext;
...
}
public class MVVMPageA : ...
{
// This is here, so both MVVMPageA and PageA can find it.
public MVVMPageB VMb;
}
public partial class PageA : ...
{
// Convenience property - our BindingContext is type MVVMPageA.
public MVVMPageA VMa => (MVVMPageA)BindingContext;
...
}
Code that creates Page B and then Page A:
var pageB = new PageB();
var pageA = new PageA();
// Tell MVVMPageA about MVVMPageB.
pageA.VMa.VMb = pageB.VMb;
Methods in MVVMPageA can now access members of MVVMPageB:
VMb.MyText("Test");
Methods in PageA can now access members of MVVMPageB:
VMa.VMb.MyText("Test");
NOTE: In this dynamic technique, if you go to Page B a second time (in the same app session), it will have a new instance of MVVMPageB.
You need a singleton viewModel for this use. I usually use one for the navbar.
So every scoped page viewModel references the singleton global viewModel inside:
PageAViewModel has property NavBarModel
PageBViewModel has property NavBarModel
and so on..
So it's obvious your button will be bind as
BackgroundColor={Binding NavBarModel.ActionColor} on every different page.
Now to have a singleton and obtain its reference i can see two ways: dependency injection (DI) or single instance creation. You can read a lot about DI on the net, while for a simple case you can have a single instance model with a prop like:
private NavBarModel _current;
public NavBarModel Current
{
get
{
if (_current == null)
_current = new NavBarModel();
return _current;
}
}
then in pages viewModels constructor set NavBarModel = NavBarModel.Current;
You would need DI though to reference more models inside your singleton, or/and make your code more reusable. Good luck.

How to show child window using Unity framework with a viewmodel of two constructors?

I'm learning MVVM and I get really rough times understanding some of his concept. My question is how to show a child window which his view model accept to constructors one for adding new object which initialize the object with new object and an other for modifie the object which passed with the second constructor. and I don't know if my way using Unity is the right way.
So basically I have two questions:
Is the way I use unity right?
How to open child window that his view model have tow constructors, one for edite mode and an other for adding mode?
this my code in app.xaml.cs:
public partial class App : Application
{
public static readonly UnityContainer container = new UnityContainer();
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
container.RegisterType<IMemberRepository, MemberRepository>();
container.RegisterType<ShellViewModel>();
var window = new Shell
{
DataContext = container.Resolve<ShellViewModel>()
};
window.ShowDialog();
}
}
and this is my code in SellViewModel for open child window:
public ShellViewModel()
{
NewMember = new DelegateCommand(ShowMethod);
}
private void ShowMethod()
{
var child = new AddMemberView
{
DataContext = App.container.Resolve<AddMemberViewModel>()
};
child.ShowDialog();
}
And this constructor in my child window:
public AddMemberViewModel(IMemberRepository repo, Member member = null)
{
_repo = repo;
SaveCommand = new DelegateCommand(SaveMember, CanSave);
if (member == null)
_Member = new Member();
else
_Member = member;
}
Edit: her I using only one constructors but in both cases how to passed the Member object in my case?
I think there is a misunderstanding of the usage of unity container, I suspect that you expect the unity container to act like a Factory pattern. Actually it is more like the Singleton pattern (not exactly the same but closer than Facotry).
I propose in your context to change a little bit your way to instantiate the view (and view model)
private void ShowMethod()
{
AddMemberViewModel vm = App.container.Resolve<AddMemberViewModel>();
vm.Member = new Member(); // Replace here with your Member object you want
// to edit
var child = new AddMemberView
{
DataContext = vm
};
child.ShowDialog();
}
You can also forget about registering this specific view model in the container (as it seems that it should not have a unique instance across your application) and use it lile this
private void ShowMethod()
{
IMemberRepository repo = App.container.Resolve<IMemberRepository>();
// Replace 'new Member()' here with your Member object you want to edit
AddMemberViewModel vm = new AddMemberViewModel(repo, new Member())
var child = new AddMemberView
{
DataContext = vm
};
child.ShowDialog();
}

Can we broadcast an object to receive in other classes

I have a class named as pendingData with a list of objects and it instantiated with the start of the application and will remain as long as application runs. But I have to change add objects to the list. How can I access that object in other view without passing the object in the constructor?
So, is there a broadcasting method or any way to do that?
And pendingData class is instantiated only once.
To ensure you only get one instance of your object you could use the singleton pattern like this
public class Singleton
{
private static Singleton instance;
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
You'll notice the constructor is private so you must obtain an instance through the Instance method. You'll also notice that method only creates an instance of the object if it doesn't exist.
You could use the same Singleton object for all your views knowing that it'll be the same one and therefore the same data.
Alternatively, you could just declare it in a central location, you main window's viewmodel perhaps, and then everything else could access it from there.
As for updating it you could pass a reference to your object to each place that it's used and then update it directly. Or you could do something with events like this
In your view's viewmodel
public static event EventHandler MyEvent;
private void OnMyEvent()
{
if (MyEvent != null)
{
MyEvent(this, new EventArgs());
}
}
In the location where your data object is, perhaps your main window's view model
MyView.MyEvent += delegate
{
// Update your data
};
If you can't have multiple views open and/or don't want your views to respond to data changes once opened then this is probably enough. However, if you want your views to respond to data in real time you could do something with events so one view can tell another view that the data has changed and that it needs to update.

How to access a custom control dependency propery from its viewmodel

I'm working on a multiple document viewer (a simple window with a custom control, each with a separate viewmodel). When clicking on a filename, a new instance of the user control is added to the main window. The user control has a dependency property which holds the path to the filename, defined in it's code-behind. Now i'm struck on how to get the value of this property from the user control to the viewmodel, so it can show the actual document. Any Hints?
<ctrl:DocViewerControl x:Key="docviewer" DocumentSource="{Binding SelectedItem.Path, ElementName=docList}"/>
I use this code in main window to make new instances of my user control where DocumentSource is the dependency property i need access to, as stated above.
Edit:
Following is the (relevant) code for the view and the viewmodel of my control, specific to the dependancy property value capture problem i have.
UserControl.xaml.cs
public partial class ToolboxControl : UserControl
{
public static readonly DependencyProperty DocumentSourceProperty = DependencyProperty.Register("DocumentSource",
typeof(string), typeof(ToolboxControl), new UIPropertyMetadata(new PropertyChangedCallback(OnDocumentSourceChanged)));
public ToolboxControl()
{
InitializeComponent();
}
public string DocumentSource
{
get { return (string)GetValue(DocumentSourceProperty); }
set { SetValue(DocumentSourceProperty, value); }
}
private static void OnDocumentSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
}
}
PV_ViewModel.cs
public class PV_ViewModel : ObservableObject
{
.....
public string DocumentSource
{
get { return (String.IsNullOrEmpty(_documentsource)? (_documentsource = #"about:blank") : _documentsource); }
set { SetField<string>(ref _documentsource, value, "DocumentSource"); }
}
.....
public PV_ViewModel()
{
PropertyChanged += DocumentSourceChanged;
}
.....
protected void DocumentSourceChanged(object sender, PropertyChangedEventArgs e)
{
if (sender != null)
{
switch(e.PropertyName)
{
case "DocumentSource":
{
// show the document and whatsoever
break;
}
}
}
}
.....
}
Neither the getter nor the setter of the viewmodel DocumentSource property get accessed from anywhere, despite the UserControl in MainWindow had is DocumentSourceProperty filled in with the current document path string. (i can see it form a collection of currently opened document on the main app).
To clarify: the application solution contains MainWindow project (the main view, a simple window with a TreeView and the UserControl container), the UserControl project (the (hopefully) standalone application used to show a single document when providing the path to the doc to show through the DocumentSource property.
I am not really sure I understand your problem (or if you understand how Dependency Properties work), so you may have to post a bit more of your code behind (with the DI for example)
Typically your DocViewerControl looks like this
public abstract class DocViewerControl : UserControl
{
public string Path
{
get { return (string)GetValue(PathProperty); }
set { SetValue(PathProperty, value); }
}
public static readonly DependencyProperty PathProperty =
DependencyProperty.Register("Path", typeof(string), typeof(DocViewerControl), new PropertyMetadata(string.Empty));
}
This will expose a Property in XAML of the control.
It's important here that you make it TwoWay binding, so any change from the UserControll will update the bounded field in your ViewModel.
Your ViewModel:
public class Doc1ViewModel : ViewModelBase {
private string path;
public string Path
{
get { return path;}
set {
if(path!=value) {
path = value;
OnPropertyChanged("Path");
}
}
}
}
Now, each time when you assign the property in your UserControl, the value in the ViewModel will be updated. As you can see, the Dependency Property consists from two properties. One static Dependency Property called PathProperty and one instance property called Path.
But having a closer look at it, it's not a real instance property at all. It just wraps calls around the Dependency Property by using GetValue and SetValue (which are derived from DependencyObject class, which every UI control inherits).
Hope this clears it up how Dependency Properties work, as it hard to tell what's wrong with your approach without seeing the code used.
In a nutshell, Dependency Properties (together with Attached Properties) extend the XAML code with TwoWay bindable properties (normal instance property can only be bound in one direction).

overriding object parameters using UNITY

I've started a project usinjg MS Unity as my IOC container and have two questions regarding overriding parameters.
public interface ITab
{
bool AllowVisible {get;set;}
}
class Tab : ITab
{
IViewModel vm;
public Tab(IViewModel vm)
{
this.vm = vm;
}
public bool allowVisible = false;
public bool AllowVisible
{
get{ return allowVisible};
set{ allowVisible = vlaue};
}
}
public interface IViewModule
{
string Name;
}
public class ViewModel
{
public string Name;
}
1) How do i set up the Tab type in unity so that i can pass in true or false to the AllowVisible property as a paramater? I dont want to have to add the
additional line of tab.AllowVisible = true; as in the case below
void Main()
{
ITab tab = unityContainer.RegisterType<ITab, Tab>();
tab.AllowVisible = true;
}
2) If i already have an instance of the ViewModel, such as vm in the case below, how do i make the container resolve the Tab object while passing in the vm object into its constructor? Currently when i resolve for the tab object, the container creates another instance of the ViewModel. I want the vm instance to get used as the tab objects viewmodel?
void Main()
{
unityContainer.RegisterType<IViewModel, ViewModel>();
unityContainer.RegisterType<ITab, Tab>();
ViewModel vm = unityContainer.Resolve<IViewModel>();
ITab tab = unityContainer.RegisterType<ITab, Tab>();
}
If you automatically want to assign a value to the AllowVisible property of your ITab implementation then you can use the InjectionProperty type provided by Unity.
You can do this in a fluent way, for example:
IUnityContainer myContainer = new UnityContainer();
myContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyObject>(
new InjectionProperty("MyProperty"),
new InjectionProperty("MyStringProperty", "SomeText"))
);
A bit more concrete:
container.RegisterType<ITab, Tab>();
container.RegisterType<ITab, Tab>(
new InjectionProperty("AllowVisible", true)
);
For more information on how to inject constructor parameters, property values...etc, check out:
http://msdn.microsoft.com/en-us/library/ff650036.aspx
As for the second part of you question, you must pass constructor parameters (IViewModel) to Unity's Resolve(...) method when you resolve an implementation for an ITab.
This question has been asked before on SO, check out:
Can I pass constructor parameters to Unity's Resolve() method?
For completeness' sake:
var viewModel = container.Resolve<IViewModel>();
container.Resolve<ITab>(new ParameterOverrides<Tab> { { "vm", viewModel} });"

Categories

Resources