passing view model from page to user control in windows store app - xaml

In a Windows Store split app, I want to pass a view model from a page to a user control. The scenario is that I want to reuse some common xaml in multiple pages, using a UserControl like a view.
In the main page:
<common:LayoutAwarePage
...
DataContext="{Binding ViewModel, RelativeSource={RelativeSource Self}}"
... >
<views:MyUserControlView Model="{Binding ViewModel}" />
...
In the user control code:
public sealed partial class MyUserControlView : UserControl
{
public static readonly DependencyProperty ModelProperty =
DependencyProperty.Register("Model", typeof(MenuSource),
typeof(MyUserControlView), null);
...
public ModelType Model
{
get
{
return this.GetValue(ModelProperty) as ModelType ;
}
set
{
this.SetValue(ModelProperty, value);
}
}
The Model setter is never called. How do I hook up the user control to the parent page's view model?
Or, is there a better way to implement shared views for use in pages?
Thanks.
-John

Correct binding would be:
<views:MyUserControlView Model="{Binding}" />
You've already set DataContext for the page above. All bindings are relative to the current DataContext.
The setter still won't be called, though. It is just a wrapper to access the DependencyProperty from code. Binding will call SetValue directly.
Depending on your requirements you might not even need to define your own Model DependencyProperty. Each control automatically inherits DataContext from its parent control. In your example above the user control already has its DataContext set to the same view model as the page.

Related

Xamarin Prism MasterDetailPage.IsPresented from child ViewModel

Does anyone have a clue on how to control IsPresented property from a child view model?
I'm implementing a custom NavBar where I want to simulate the Hamburger icon behavior so my child page on load has
NavigationPage.SetHasNavigationBar(this, false);
which hides the navigation par.
Inside Xaml file I have a button which I want to bind to a PropertyCommand of child viewmodel and show Master page, basically somehow to call Master's IsPresented.
Thanks.
There are a couple of ways to go about it.
The way I would do it would be to use MVVM and use an interface to access the 'presenting the Master page' functionality where its needed.
public interface ICustomMasterDetail
{
void SetMasterPresented(bool isPresented);
}
Now extend on the MasterDetailPage and also implement the above interface
public class CustomMasterDetail : MasterDetailPage, IRootMasterDetail
{
public CustomMasterDetail() : base()
{
//constructor code
}
public void SetMasterPresented(bool isPresented)
{
IsPresented = isPresented;
}
}
Using using an IoC container to register and resolve the interface will let you use its functionality from where ever you want.
The other solution would be to just use a static variable to store the instance of your MasterDetailPage and access that directly to change the IsPresented property

Prism MVVM Binding to User Control Dependency Property

I have a Universal app that I'm writing and I want to use Prism and Unity as my MVVM framework. Everything was going great until I got to a view where I have multiple instances of the same user control(a custom Watermark Textbox). For some reason, I haven't been able to find a good solution to my problem. I imagine I'm overlooking something and there is a straightforward answer.
Here's my source code(just the relevant portions). Some background, I had this working before implementing Prism. The Commands attached to the user controls are firing as expected but I can't figure out how to manipulate the control itself):
Any guidance on how to use user controls with Prism or binding to dependency properties with Prism would be great. Thanks.
My View
<!-- I want to be able to set the watermark and also retrieve the text from my ViewModel -->
<uc:WatermarkTextBox Width="250"
x:Name="FullName">
<i:Interaction.Behaviors>
<iCore:EventTriggerBehavior EventName="GotFocus">
<iCore:InvokeCommandAction Command="{Binding EntryFieldFocus}"
CommandParameter="{Binding ElementName=FullName}"/>
</iCore:EventTriggerBehavior>
</i:Interaction.Behaviors>
</uc:WatermarkTextBox>
<!-- This one as well -->
<uc:WatermarkTextBox Width="250"
x:Name="EmailAddress">
<i:Interaction.Behaviors>
<iCore:EventTriggerBehavior EventName="GotFocus">
<iCore:InvokeCommandAction Command="{Binding EntryFieldFocus}"
CommandParameter="{Binding ElementName=EmailAddress}" />
</iCore:EventTriggerBehavior>
</i:Interaction.Behaviors>
</uc:WatermarkTextBox>
Going from your comments/error message ("Failed to assign to property %0") when attempting to bind the property directly, the Property you are trying to bind to isn't a dependency property.
Your WatermarkTextBox only implements a CLR Property, like this:
public class WatermarkTextBox : TextBox {
public object Watermark { get; set; }
}
This can't be used for databinding.
You need to implement it as Dependency Property, like
public class WatermarkTextBox : TextBox {
public object Watermark
{
get { return (object)GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
public static readonly DependencyProperty WatermarkProperty =
DependencyProperty.Register("Watermark", typeof(object), typeof(WatermarkTextbox), new PropertyMetadata(null));
}
This will allow to use <uc:WatermarkTextBox Watermark={Binding watermarkText, Mode=TwoWay} for example.
If you can't modify WatermarkTextBox (third-party closed source control), then you'll have to implement an attached behavior and then bind your ViewModel Property to the attached behavior and assign the Watermark property from within it.
If you need TwoWay binding (which is unlikely in case of Watermark property, since it won't be changed from UI), you have to register an event handler (KeyDown in linked answer) to this specific event and pass the new value to the attached property
Except these two ways, there is no way to update a Control's CLR property without violating MVVM.

How to access Pivot.TitleTemplate as UserControl?

Subj, how can i get it?
<controls:Pivot.TitleTemplate>
<DataTemplate>
<mainPivot:MyUserControl Name="MainPivotHeader"/>
</DataTemplate>
</controls:Pivot.TitleTemplate>
Tried to find it via VisualTreeFinders, but it sees only pivot item.
UserControl shows a picture, but it depends on user. During first initialization, it is empty, because user is not yet logged in. So, i'd like to force its update.
I can use mvvm light messaging, but i'm looking for self-sufficient components. This forcing is rare, so i dont want to use messaging here.
You should bind the Title property of the Pivot to a property on a ViewModel. Your DataTemplate would then have it's DataContext already set to that object. When you need to refresh, you call some method on that object.
Example
public class ViewModel : INotifyPropertyChanged
{
private MyTitleObject _titleObject;
public MyTitleObject TitleObject
{
get { return _titleObject; }
set
{
_titleObject = value;
OnPropertyChanged("TitleObject");
}
}
public void Refresh()
{
TitleObject = new MyTitleObject();
// or refresh values directly on the object
}
...
}
You xaml for your Pivot would need to following
<controls:Pivot Title="{Binding TitleObject}">
</controls:Pivot>
When you want to refresh, call the refresh on the viewmodel.

Exposing commands for MVVM user control inside GridView in WinRT

I have a Windows Store application, following the MVVM pattern.
I have a Parent View (with matching Parent ViewModel) that contains a GridView control.
The ItemTemplate for that GridView control contains a Child View.
That child view contains a couple of buttons.
How do I wire it up such that when a user clicks a button on one of the ChildView controls, a method is called on the Parent ViewModel?
There are two methods for doing this.
first one that you can use is - bind your button to a command that is defined- in your parent viewmodel where you can do your work.
second one is - you can use mvvm messenger class. in which you have to send message from your button click eventhandler to your viewmodel. when you received this message add some eventhandler to it and perform your work there.
This is how I went about solving this problem.
Add an ICommand backed Dependency Property on the Child View code behind.
public static readonly DependencyProperty ChildButtonCommandProperty = DependencyProperty.Register("ChildButtonCommand", typeof(ICommand), typeof(ChildView),new PropertyMetadata(null, OnChildButtonCommandChanged));
public ICommand ChildButtonCommand
{
get { return (ICommand)GetValue(ChildButtonCommandProperty); }
set { SetValue(ChildButtonCommandProperty, value); }
}
private static void OnChildButtonCommandChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var self = (ChildView)sender;
self.ChildButtonCommand.Command = (ICommand)e.NewValue;
}
In the Parent ViewModel, add a public getter property of type ICommand, implemented with a RelayCommand that you can find here: https://relaycommandrt.codeplex.com/
In the Xaml of the Parent View, bind the ChildButtonCommand in the Child View:
<GridView.ItemTemplate>
<DataTemplate>
<views:ChildView ChildButtonCommand="{Binding ElementName=ParentView, Path=DataContext.PropertyOnParentViewModel}"/>
</DataTemplate>
Examine the binding syntax closely. Since we're in a DataTemplate for the GridView Item, our DataContext is not the Parent View Model.(It's the child item objects). If we want to bind the button command to the Parent View Model, we need a reference to something in our parent view. In this case, I named the view "ParentView". Using the Binding ElementName syntax, I could bind to the DataContext of the ParentView and more specifically a property on the ParentViewModel.

Getting Unity to Resolve views in XAML

I'm starting out with MVVM, and I'm starting to understand things. I'm currently experimenting with the Cinch framework, though I'm not committed to it as of yet.
I was injecting the ViewModels into the Views using by having a reference to the ViewModel in the codebehind of the view, with the property having a [Dependency] on it, and in the setter it sets the DataContext to the right view, using Unity. Neat trick, I thought.
I'm trying to get my app to work as a single Window, with injected views (As opposed to multiple windows and dealing with opening\closing them)
I changed my views from Windows to UserControls, and added a to the main window.
That worked, but the ViewModel was never injected, presumably because the XAML doesn't use Container.Resolve to create the view, as when I created the view and added it manually in the code-behind using Resolve, the [Dependency] was created.
How can I set up my window, so that if I add a view through XAML, or the view gets changed as a result of a UI action etc, it gets it through Unity, so that it can work its magic?
This problem is normally solved using Regions and the RegionManager. In the main window ViewModel, a set of Regions is created and added to the RegionManager. Then ViewModels can be Resolved and added to the Region.Views collection.
In XAML, the Region is normally injected by having the ItemsSource property of an ItemsControl bound to the region property of the main ViewModel.
So, in the main screen ViewModel you would have something like this:
public class TestScreenViewModel
{
public const string MainRegionKey = "TestScreenViewModel.MainRegion";
public TestScreenViewModel(IUnityContainer container, IRegionManager regionManager)
{
this.MainRegion = new Region();
regionManager.Regions.Add(MainRegionKey, this.MainRegion);
}
public Region MainRegion { get; set; }
}
This would be Resolved normally in your IModule
#region IModule Members
public void Initialize()
{
RegisterViewsAndServices();
var vm = Container.Resolve<SelectorViewModel>();
var mainScreen = Container.Resolve<TestScreenViewModel>();
mainScreen.MainRegion.Add(vm);
var mainView = ContentManager.AddContentView("Test harness", mainScreen);
}
#endregion
And the XAML representation of your template looking something like
<DataTemplate DataType="{x:Type TestModule:TestScreenViewModel}">
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto">
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=MainRegion.Views}" />
</StackPanel>
</ScrollViewer>
</DataTemplate>
The way to solve your problem is to make your window to have a ViewModel as well, with ViewModels of UserControls exposes as properties on it. Then in your XAML for a window you'd simply use Binding mechanism to bind UserControl's DataContexts to proper properties of your your main ViewModel. And since that main ViewModel is resolved from Unity container it would have all other ViewModel-s injected as needed.