I have a WPF application with two pages, now I wanted to navigate to the other page when the button in first the page is clicked (I wrote the command for button in the first page), but the logic should be through the viewmodel. How to achieve this?
When I write WPF applications that need to navigate to different pages, I like to follow Rachel Lim's method to implement it using DataTemplates and ViewModels. You can follow the link to her page to get the exact code for the solution, but I'll give a little summary of her method here.
In her method, she creates a ViewModel that represents the application and has a property called CurrentPage which holds a ViewModel. You can then create a command on the ApplicationViewModel called ChangePage. This command will take the ViewModel that is passed as a parameter and sets it to the CurrentPage.
The xaml takes the responsibility of switching out the correct views. When using this method, I put a ContentControl in my MainWindow and bind the Content property to ApplicationViewModel.CurrentPage. Then in the resources of the MainWindow, I create DataTemplates to tell the view "When I try to display this ViewModel, put that View on the screen".
You don't really provide any code. But I assume your Navigation is in your code behind. You could do this by binding a Command OneWayToSource.
XAML
<local:MainWindow x:Class="WpfNameSpace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfNameSpace"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
NavigatePageCommand="{Binding Path=MyViewModel.NavigateCommand, Mode=OneWayToSource}"
Title="MainWindow" Height="600" Width="800">
<Grid>
</Grid>
</local:MainWindow>
Please take a look at local:MainWindow.
C#
public partial class MainWindow : Window
{
public ICommand NavigatePageCommand
{
get { return (ICommand) GetValue(NavigatePageCommandProperty); }
set { SetValue(NavigatePageCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for NavigatePageCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NavigatePageCommandProperty =
DependencyProperty.Register("NavigatePageCommand", typeof(ICommand), typeof(MainWindow),
new PropertyMetadata(0));
public MainWindow()
{
InitializeComponent();
NavigatePageCommand = new RelayCommand(Navigate);
}
public void Navigate()
{
//Do Navigation here
}
}
I assume you are familiar with Commands, ViewModels and Bindings and you get the idea.
Related
I have a custom control which will have properties that can be set which will affect the logic of how the control is handled. How should this be handled in MVVM?
Currently I'm stuck trying to pass a DependencyProperty to the ViewModel.
Example code:
CustomControl.xaml
<UserControl x:Name="Root" ...>
<UserControl.DataContext>
<local:CustomControlViewModel SetDefaultValue="{Binding ElementName=Root, Path=SetDefaultValue, Mode=TwoWay}"/>
</UserControl.DataContext>
...
</UserControl>
CustomControl.xaml.cs
...
public static readonly DependencyProperty SetDefaultValueProperty = DependencyProperty
.Register("SetDefaultValue",
typeof(bool),
typeof(CustomControl),
new FrameworkPropertyMetadata(false));
public string SetDefaultValue
{
get { return (string)GetValue(SetDefaultValueProperty ); }
set { SetValue(SetDefaultValueProperty , value); }
}
...
CustomControlViewModel.cs
...
private bool setDefaultValue;
public bool SetDefaultValue
{
get { return setDefaultValue; }
set
{
if (setDefaultValue!= value)
{
setDefaultValue= value;
OnPropertyChanged("SetDefaultValue"); // INotifyPropertyChanged
}
}
}
...
My goal with this property specifically is to be able to set a default value (getting the default value requires running business logic). So in another view I would use this control like this:
<local:CustomControl SetDefaultValue="True"/>
(Before I answer I want to point out that what you have here is actually a user control, not a custom control. That's not nit-picking on my part; A user control is something derived from the UserControl class and it typically has an associated XAML file. A custom control just derives from the Control class and has no associated XAML file. A custom control requires you set to a control template. Custom controls can be styled. User controls cannot.)
The thing about UserControl is that sometimes we create one assuming one specific DataContext, of one type and then we make all of its XAML bind to that object type. This is good for big, main pages of an application that are not meant to be re-used in too many places
But another approach -- that you have started to do here -- is to give our user controls their own dependency properties. So in this case, why not dispense with the need for this control to have any specific DataContext altogether? This is the first step to making user controls truly re-usable in many places.
Unless this control is huge, there's a good chance that When you are laying out its XAML, it can get everything that XAML needs to bind to in just a few properties. So why not make all those properties into dependency properties and make the control's XAML bind to itself?
Make the class be its own DataContext. Set that property on the root UI element of the control's layout and then every Binding should work well.
To illustrate, I've renamed your control class MyUserControl I've renamed your "SetDefaultValue" property to just be "BoolOption" Let's assume that all it needs to show is a checkbox, representing the bool value and a string label on the checkbox. We can do this with just two dependency properties. (In effect, this entire control is now just a pointless, glorified CheckBox control but please ignore that)
MyUserControl.xaml.cs
public static readonly DependencyProperty BoolOptionProperty =
DependencyProperty.Register(nameof(BoolOption),
typeof(bool),
typeof(MyUserControl),
new FrameworkPropertyMetadata(false));
public string BoolOption
{
get { return (string)GetValue(BoolOptionProperty ); }
set { SetValue(BoolOptionProperty , value); }
}
public static readonly DependencyProperty CheckBoxLabelProperty =
DependencyProperty.Register(nameof(CheckBoxLabel),
typeof(string),
typeof(MyUserControl),
new FrameworkPropertyMetadata(string.Empty));
public string CheckBoxLabel
{
get { return (string)GetValue(CheckBoxLabelProperty ); }
set { SetValue(CheckBoxLabelProperty , value); }
}
// Constructor. Here we set the control to be its own UI's DataContext
public MyUserControl()
{
InitializeComponent();
// Make us be the UI's DataContext. Note that I've set the
// x:Name property root Grid in XAML to be "RootUiElement"
RootUiElement.DataContext = this;
}
MyUserControl.xaml
<UserControl x:Class="MyCompany.MyApp.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyCompany.MyApp.Controls"
x:Name="Root"
d:DesignHeight="450"
d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:MyUserControl}}"
mc:Ignorable="d">
<Grid x:Name="RootUiElement">
<CheckBox IsChecked="{Binding BoolOption}"
Content="{Binding CheckBoxLabel"
/>
</Grid>
</UserControl>
Finally you could use the control anywhere you wanted, no matter what your current DataContext is, like this
<local:MyUserControl BoolOption="True" CheckBoxLabel="Is Option A enabled?"/>
<local:MyUserControl BoolOption="False" CheckBoxLabel="Is Option B?"/>
Or even bind it to some other DataContext where you're using it like this. Suppose my current DataContext is a view-model that has a boolean UserBoolOptionC property
<local:MyUserControl BoolOption="{Binding UseBoolOptionC}" "Is Option C enabled?"/>
I am currently writing a WPF application which has a navigation panel on its left side (to which I bind a navigationViewModel) and a content presenter on its right side (which I bind to the previously mentioned VM's UserControl member 'CurrentView'.
For each item of this navigation panel, I created a corresponding user control and for each of these user controls, I bind an instance of a corresponding ViewModel.
Clicking on a navigation panel's items sets its ViewModel's UserControl member CurrentView to the instance of the corresponding UC, which is then displayed in the content presenter mentioned above.
The first navigation item is some "select or create a new client" form. When this operation is done, I would like to set some wide app resource id to which I would bind the other navigation items Enabled state. Thus, if the wide app resource is null, they are disabled, as soon as it is set to anything, they are enabled. There would also be some mechanism which would allow the corresponding ViewModel to be notified of this situation.
I am wondering if this would be considered a good practice?
Furthermore, I'd like to know if I can simply declare an int resource in the app.xaml and bind it to the navigation items Enabled property, would setting this resource to anything immediately refresh this property? Or is there a better, simpler or cleaner way?
An alternative could be to nest the two viewmodels (navigation and currentview) in a third viewmodel (say mainviewmodel)
This main viewmodel could then keep state that should be available across these viewmodels and across instances of currentviews.
This way you do not need to have global state in the application and you can simply set the datacontext of the Window to the main viewmodel and bind the navigation and content views to properties of the main viewmodel.
This also allows you to have a proper place for navigating to a different content view.
Here is what I ended up doing:
In my app.xaml; I declare the following resource:
<Application x:Class="MyProject.GUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyProject.GUI.ViewModels"
StartupUri="MainWindow.xaml">
<Application.Resources>
<vm:MainViewModel x:Key="MainViewModel" />
</Application.Resources>
</Application>
This MainViewModel exposes a static property as follow:
static bool _myStaticProperty;
public static bool MyStaticProperty
{
get
{
return _myStaticProperty;
}
set
{
_myStaticProperty = value;
NotifyStaticPropertyChanged("MyStaticProperty");
}
}
And the following static INPC mechanism:
#region Static property INPC mechanism
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
static void NotifyStaticPropertyChanged(string propertyName)
{
if (StaticPropertyChanged != null)
{
StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
And my different viewmodels:
FirstChildViewModel _firstChildViewModel;
public FirstChildViewModel FirstChildViewModel
{
get
{
if (_firstChildViewModel == null)
_firstChildViewModel = new FirstChildViewModel();
return _firstChildViewModel;
}
}
//then a second one, a third one and so on
Which are binded to my user controls as follow
<UserControl x:Class="MyProject.GUI.Views.FirstChildControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyProject.GUI.ViewModels"
mc:Ignorable="d"
DataContext="{Binding Path=FirstChildViewModel,
Source={StaticResource MainViewModel}}">
In my user controls' xaml code, I declare command bindings etc. which basically do the following in their ViewModel
MainViewModel.MyStaticProperty = myBoolValue;
Is it possible to call a custom dependency property in the XAML of the element in which it is defined?
I mean, i have the following simple code for my mainWindow:
Code
public partial class MainWindow : Window
{
public static readonly DependencyProperty SpecialToProperty = DependencyProperty.Register("SpecialTo", typeof(double), typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
}
public double SpecialTo
{
get
{
return (double)GetValue(SpecialToProperty);
}
set
{
SetValue(DoubleAnimation.ToProperty, value);
}
}
}
How can i use that dependency property from the XAML partial code of the MainWindow class?
I mean something like:
<Window x:Class="WpfAnimationTEst.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
SpecialTo=200>
I know it can be done using attached dependency properties, but is it the only way? Is it not possible to call a dependency property defined in the code-behind?
Thank you and sorry if the question is some kind of stupid, i'm just learning and trying to understand WPF.
I found the answer after I initially posted a wrong answer:
The problem really lies in circular dependencies if you use andreask's answer. I had to create a BaseClass for all windows:
1) Create a new Window Base Class:
public class BaseWindow : Window {
public BaseWindow() { }
public static readonly DependencyProperty SpecialToProperty = DependencyProperty.Register("SpecialTo", typeof(double), typeof(BaseWindow));
public double SpecialTo {
get {
return (double)GetValue(SpecialToProperty);
}
set {
SetValue(SpecialToProperty, value);
}
}
}
This will be the new baseclass for all your windows.
2) Modify your MainWindow xaml: (Change YOURNAMESPACE (2x) to your namespace name)
<local:BaseWindow x:Class="YOURNAMESPACE.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YOURNAMESPACE"
Title="MainWindow" Height="350" Width="525" SpecialTo="100">
<Grid>
</Grid>
</local:BaseWindow>
3) And you also need to modify your partial MainWindow.cs:
public partial class MainWindow : BaseWindow {
public MainWindow() {
InitializeComponent();
}
}
That worked for me, however, you will always need to use the extra xaml markup in your window declaration.
I'm answering my own question because there seems to be many ways to solve it correctly. I've upvoted the answers that best helped me, but i can't set any as the correct answer since all are correct.
So i'll just post a conclusion. If you think that i'm mistaken, please post a comment and i will correct my mind.
The main answer to my question is no, it is not possible to directly call a custom dependency property defined at code-behind from its "linked" XAML file. It is mandatory to instantiate the control in which the property is defined to call it.
To me, the best workarrounds to use a custom dependency property in XAML, defined in the code-behind are the posted by #Clemens and #Noel Widmer. This and this
You can use custom dependency properties in XAML, but only if you instantiate the control in XAML. For example, take a customized TextBox element:
public class MyTextBox : TextBox
{
public static readonly DependencyProperty SpecialToProperty = DependencyProperty.Register("SpecialTo", typeof(double), typeof(MyTextBox));
public double SpecialTo
{
get
{
return (double)GetValue(SpecialToProperty);
}
set
{
SetValue(DoubleAnimation.ToProperty, value);
}
}
}
You can of course create an instance of MyTextBox in XAML and assign the SpecialTo property there:
<custom:MyTextBox SpecialTo="1.0" />
In your case, however, you're not instantiating the custom class MainWindow, but you create a new instance of class Window, and the Window class isn't aware of the custom dependency property (the SpecialTo property is not even available in Window, since you declared it within the MainWindow class).
For the dependency property to be recognized, you'd need to instantiate MainWindow directly:
<custom:MainWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
SpecialTo=200>
However, this means you need to omit the x:class directive that used to combine XAML and codebehind of your window (otherwise you'd run into circular dependencies), and I'm not sure if this correctly initalizes your window...
Yes, it is possible. Dependency properties are used to bind within XAML. If you want to bind to property defined in the code behind window you need to reference this window as XAML element, i.e. add tag for your main window x:Name="mainWindow", and next in the binding expression refer it as ElementName=mainWindow
Let's say I have a custom control which wraps another control (for example MyCustomButton). I expose a property Content, which wraps the inner control:
public object Content
{
get { return innerControl.Content; }
set { innerControl.Content = value; }
}
In order for a consumer to bind to this property, I need to define a DependencyProperty for it:
public static DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof (object), typeof (MyCustomButton));
but now I need my property definition to use GetValue/SetValue:
public object Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
so I'm not wrapping the value of the inner control anymore.
I can define PropertyMetadata to handle the PropertyChanged event of the DependencyProperty, but then I need a bunch of plumbing code to keep the values in sync and prevent infinite loopbacks on changed.
UPDATE: I can't just derive from Button because my UserControl has various other concerns.
Is there a better way to do this?
Well, depending on the particulars of why you're wrapping a button with a user control, you could define a custom control that inherits from button. Then, instead of wrapping the button and exposing the wrapped methods and properties that you want, you can simply override methods and properties whose behavior you want to define the custom control. This way, you'll get all of the functionality of button without the need to reinvent the wheel.
Here's a google link that walks you through it (one of the first that I found - there are plenty): http://knol.google.com/k/creating-custom-controls-with-c-net#
If the user control has other concerns, this may not be an option for you, but I'm offering this answer because the only purpose that you've mentioned for it is wrapping the button. I'd personally favor creating a custom control and inheriting rather than a user control and wrapping if the control in question is simply meant to be a more specific kind of wrapped/inherited control (i.e. button in your case).
Edit: In light of updated question...
You could do something along these lines. Here is the XAML of the client of your user control:
<Grid>
<local:MyControl ButtonContent="Click Me!"/>
</Grid>
</Window>
Here is the XAML for the user control itself:
<UserControl x:Class="GuiScratch.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:GuiScratch"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<ContentControl Content="Asdf"/>
<Button Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyControl}},Path=ButtonContent}"/>
</StackPanel>
</Grid>
</UserControl>
And, here is the code behind:
public partial class MyControl : UserControl
{
public static readonly DependencyProperty ButtonContentProperty =
DependencyProperty.Register("ButtonContent", typeof(object), typeof(MyControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public object ButtonContent
{
get { return (object)GetValue(ButtonContentProperty); }
set { SetValue(ButtonContentProperty, value); }
}
public MyControl()
{
InitializeComponent();
}
}
So, you don't need to handle the binding at all through code. Your client XAML binds to your dependency property, as does the XAML of the user control itself. In this fashion, they share the dependency property setting. I ran this in my little scratchpad, and the result is (at least my understanding of) what you're looking for. The main window displays the user control as a stack panel with the text "Asdf" and then a button with the text "Click Me!"
I am learning Silverlight. In the process, I'm trying to build a custom user control. My ultimate goal is to be able to write the following statement in XAML:
<my:CustomControl>
<my:CustomControl.MainControl>
<Canvas><TextBlock Text="Hello!" /></Canvas>
</my:CustomControl.MainContent>
</my:CustomControl>
The content of the control will be wrapped in a custom border. Once again, this is just a learning exercise. To append my border, I have create the following UserControl:
<UserControl x:Class="CustomControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Grid x:Name="LayoutRoot">
<Border>
<!-- CustomControl Content -->
</Border>
</Grid>
</UserControl>
The code-behind for this file looks like the following:
public partial class CustomControl : UserControl
{
public UIElement MainContent
{
get { return (UIElement)GetValue(MainContentProperty); }
set { SetValue(MainContentProperty, value); }
}
public static readonly DependencyProperty MainContentProperty =
DependencyProperty.Register("MainContent", typeof(UIElement), typeof(CustomControl),
new PropertyMetadata(null));
public CustomControl()
{
InitializeComponent();
}
}
The thing I am having a problem with is getting the MainContent to appear in my CustomControl. I am confident that I am setting it properly, but I'm not sure how to display it. I really want it to be a DependencyProperty as well so I can take advantage of data binding and animations.
How do I get the MainContent to appear in the CustomControl? Thank you
First you need to wait until the rest of the control has been parsed so you need to hook the loaded event:-
public CustomControl()
{
InitializeComponent();
Loaded += new RoutedEventHandler(CustomControl_Loaded);
}
Then in the loaded event assign your MainControl property to the Child property of the border. To do that its best if you give your Border an x:Name which for now I'll simple call "border".
void CustomControl_Loaded(object sender, RoutedEventArgs e)
{
border.Child = MainControl;
}
That'll get you going. Of course you may need to deal with the MainControl property being changed dynamically so you need add a bool isLoaded field in your control and set that in the loaded event. When true your MainControl setter should assign the incoming value to the border.Child.
Things can start to get more complicated and in fact I don't recommend this approach to creating a custom control. For a better approach see Creating a New Control by Creating a ControlTemplate