I am creating a page-based WPF application using MVVM. I have created a custom (non dependency object) helper class to centralize navigation. This class is created as a resource of my main window like so.
<Window.Resources>
<local:NavigationManager x:Key="NavigationManagerKey" x:Name="NavigationManager"/>
</Window.Resources>
The class contains an ICommand that I have exposed publicly so that it can be used in XAML. However, I am struggling to find out how to bind to it. I would prefer not to have to set it as the data context for the page as that is already in use. Normally, I bind to a command like so (when I am binding to a command on the data context)
<Button Header="Image" Command="{Binding CreateImageAssetCommand}"></Button>
Thanks for any help with the matter.
You can set the source of the binding:
<Button Header="Image" Command="{Binding CreateImageAssetCommand, Source={StaticResource NavigationManagerKey}}"></Button>
Related
In Xamarin Forms, I defined a custom data template like so:
<DataTemplate x:Key="MyControlDataTemplate">
<ViewCell>
<controls:MyControl/>
</ViewCell>
</DataTemplate>
This snippet belongs to a ListView in MainPage.xaml which has its binding context set to MainPageViewModel.cs
Inside MyControl, I want to bind to a property of MainPageViewModel.
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{set binding here to MainPageViewModel property}" />
</Label.GestureRecognizers>
MainPageViewModel has this command property:
public Command<MyItem> LabelTappedCommand { get; set; }
The idea is to create a single Command object which gets shared between each cell by also setting the CommandParameter property, but I'm stuck with the Command property right now.
Is this possible?
This is certainly possible with 2 easy steps.
Give your xaml page an x:Name=MainPage.
Reference it in the binding like this Command="{Binding Path=BindingContext.MyProperty, Source={x:Reference MainPage}}"
This is under the assumption that the BindingContext of your main page is set to your MainPageViewModel but either way you get the idea.
I think that the only solution is to expose Command binding through the MyControl.cs. After that you can use the solution that references some parent control as the binding context (described in answer by #Knoop and you said you are aware how it works so I am not going into details regarding this).
I use caliburn.micro in WPF.
Although i set x:Name to my element(which is in xaml), i do not see elements (button, textbox, grid etc) in my code behind(In my ViewModel).
As if x:Name is private
For Example:
<Button x:Name="mybutton">
<StckPanel Orientation="Horizontal">
<Image Width="30" Source="/Resources/edit.png" />
<TextBlock Margin="5" FontSize="15" Text="Update">
</TextBlock>
</StackPanel>
</Button>
X:Name doesn't expose your View Elements to ViewModel, in fact, as Clemens rightly pointed out, you shouldn't try to access the View Elements from View Model.
If you are intending to Invoke a method on Button Click (from above xaml) using the Caliburn Micro conventions, you should write a method that corresponds to the x:Name. For example,
public void mybutton()
{
// Do something
}
This would invoke mybutton() method each time your button with x:Name mybutton is clicked.
You cant access your XAML elements with name in ViewModel. You can only access your XAML elements in code behind file only. For example, your XAML is MainWindow.xaml, then you can access XAML elements in MainWindow.xaml.cs file only. The main purpose of applying MVVM pattern to a project is to make view (xaml) and business logic (ViewModel) loosly coupled. Read more about MVVM here.
I know there are a lot of questions about WPF navigation, for application developed with MVVM pattern, and I have read tens and tens of answers but I'm missing probably something.
I started building an application following Rachel's article here. All works just fine, there's an ApplicationView Window with this XAML:
<Window x:Class="CashFlow.ApplicationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:CashFlow.ViewModels"
xmlns:v="clr-namespace:CashFlow.Views"
Title="ApplicationView" Height="350" Width="600" WindowStartupLocation="CenterScreen">
<Window.Resources>
<!--Here the associations between ViewModels and Views-->
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<v:HomeView />
</DataTemplate>
</Window.Resources>
<!--Define here the application UI structure-->
<DockPanel>
<Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
<ItemsControl ItemsSource="{Binding PageViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding}"
Margin="2,5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<ContentControl Content="{Binding CurrentPageViewModel}" />
</DockPanel>
The ApplicationViewModel, that is set as DataContext for this window when the application starts, maintains an ObservableCollection of my ViewModels. Thanks to data templates, it's possible to associate every view with its viewmodel, using a ContentControl to render the views. Navigation in this case is accomplished with a "side bar" of buttons, binded to ApplicationViewModel commands that perform the changes of CurrentPageViewModel object.
I'm wondering how I can perform navigation without the presence of that sidebar of Buttons. Having only the Content control, I should be able to change the CurrentPageViewModel from the others viewmodel? Probably the answer will be very trivial, but I can't see that right now.
Your top level homeviewmodel can orchestrate navigation via an eventbus pattern. To use eventbus, you would inject an object that tracks objects that want to be notified of events. Then when a view model raises an event, the homeviewmodel receives it and performs the currentpageviewmodel assignment that will navigate you to the next viewmodel.
Ex:
Messenger defines two methods - RegisterForEvent<IEvent>(ViewModel aViewModel), and RaiseEvent(IEvent event).
So you would define a function to subscribe to the events -
HomeViewModel.cs
...
void SubscribeForEvents() {
Messenger.RegisterForEvent<NavigationEvent>(this);
}
Then you inject the Messenger into your other view models, and from those view models, raise the event:
Messenger.RaiseEvent(new NavigationEvent { TargetViewModel = new TargetViewModel() });
Where the event is something like
public class NavigationEvent : IEvent {
ViewModel TargetViewModel { get;set;}
}
C Bauer is right with what you are missing. I found in order to switch the data context, you'll need a messenger service to flag your "applicationviewmodel" to switch its data context. A good discussion with the steps you need are spelled out in a discussion here.
Register the message to be received in your applicationviewmodel, then handle the data context switch in your receive message function.
Also, this might be true or not, but I had to use 1 window, with multiple user controls as opposed to multiple windows if I wanted to have 1 window showing at all times. Lastly, I followed Sheridan's example and defined my data templates in my app.xaml as opposed to the window itself.
I have a listbox in WPF that will contain a list of ResultsViewModel items, however the actual runtime type of these objects could be
CalculateResultsViewModel,
ScenarioResultsViewModel,
GraphResultsviewModel etc etc,
all of which extend the base abstract class ResultsViewModel.
Each of these view models should be rendered differently in the ListBox so needs a different DataTemplate. I can do that just with XAML easy enough. The difficulty is that when the viewmodels are either "processing" or when they have failed", I need them to display a DataTemplate for "processing" or "errored" which I can only so far do with Triggers. That however then means I can't use the DataTemplateSelector or a basic XAML style.
The only solution I can think of (not clean I know) is to set the DataTemplate programmatically in the SetResult() method of each viewmodel class, which is what gets called when the processing completes either successfully or with an error. In that DependencyProperty I can look at the return code and then programatically set the DataTemplate depending on the sucess/failure result. The only problem is I cannot figure out how to
Obtain a DataTemplate resource from a ResourceDictionary just using c# code - bearing in mind Im calling all of this from the viewmodel class, not the window code-behind .xaml.cs file so it doesn't have access to the properties of Window
having only a handle to the viewmodel class, somehow obtain a reference to the ListBoxItem that contains it and then programmatically set the DataTemplate on this container.
Can anyone point me in the right direction?
you can take the magic with implicit datatemplates
<ListBox ItemSource={Binding YourResults}>
<ListBox.Resources>
<DataTemplate DataType={x:Type CalculateResultsViewModel}>
<Grid></Grid>
</DataTemplate>
<DataTemplate DataType={x:Type ScenarioResultsViewModel}>
<Grid></Grid>
</DataTemplate>
<DataTemplate DataType={x:Type GraphResultsviewModel }>
<Grid></Grid>
</DataTemplate>
</ListBox.Resources>
</ListBox>
for "processing" or "errored" viewmodels you can specify a adorner overlay in all yout datatemplates (ok but you must use the triggers)
hope this helps
I have a dependency property on a user control called "RefreshCommand" (ICommand type) that I want to bind to a Command in my main window.
If I write this in code, it works...
MainToolbar.RefreshCommand = (ICommand)this.CommandBindings[0].Command;
.. but how can I express that in XAML?
I would additionally like to refer to the command by its name as opposed to its index.
Thanks,
you can bind in XAML to other XAML Element through e.g.
The Element name:
RefreshCommand="{Binding ElementName=window, Path=CommandBindings[0].Command}"
On Properties of "yourself"
RefreshCommand="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=CommandBindings[0].Command}"
Upwards along the tree with AncestorType
RefreshCommand="{Binding Path=CommandBindings[0].Command, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
If the toolbar is a child from window, I suppose the third should work well.
I would recommend doing this then.
3 Classes
MainWindow.xaml (Window)
MainViewModel.cs (Class)
UserControl.xaml (UserControl)
Set the data context of the MainWindow to the ViewModel. The UserControl will inheret this data context unless you change it explicitly. Set up the controls on the user control / main window to bind to relay commands on the Main View Model.
This will give you the context you need without hard coding indexes (xxx.[0]) and still maintain a degree of separation.