Referencing MainWindow's content in MaterialDesign's DialogHost - wpf

I'm developing a WPF application using Material Design in XAML library. I'd like to use a dialog box to display error messages. In the documentation I've read that in order to dimm and disable content behind the dialog box I have to put it in the DialogHost tag, right after DialogHost.DialogContent
This is what I have right now:
<Window>
<md:DialogHost>
<md:DialogHost.DialogContent>
Content of my dialog box
</md:DialogHost.DialogContent>
My window's content wrapped in grid.
</md:DialogHost>
</Window>
The problem is: I'm planning to add few more dialog boxes for different purposes and I don't really know how to do that, since I have to put the rest of the code inside the DialogHost tag, which in my opinion would be a bit messy.
Instead I would like to achieve something like this:
<Window>
<Grid>
<md:DialogHost>
<md:DialogHost.DialogContent>
Content of my dialog box
</md:DialogHost.DialogContent>
Reference somehow the rest of the window's content
</md:DialogHost>
Window's content
</Grid>
</Window>
I tried using ContentPresenter but I'm getting error saying that the property Content cannot be bound to visual element.
If the idea described above is impossible to do, how can I use more than 1 dialog boxes? Because nesting one in another would result in a big messy code.

You should first remove the <md:DialogHost.DialogContent>from your main window and create an <UserControl>for each dialog box you need.
In the ViewModel class using such a dialog you must instantiate this <UserControl> and provide this instance as parameter for the DialogHost.Show method.
Dim view As New MyDialog1() With {.DataContext = Me}
Dim obj as Object = Await MaterialDesignThemes.Wpf.DialogHost.Show(view)
if obj IsNot Nothing Then
'do something
EndIf
I this (VB) example an MyDialog1 View class is instantiated using the DataContext of the VieModel class allowing the View class to access ViewModel class properties. Then the DialogHost.Show method is invoked. The View class can provide user response which is evaluated after closig of the View class.

Related

WPF - Creating a custom control with dynamic subcontrols

I am attempting to create a menu/navigation control in WPF that will be used across several applications. The control is intended to reside in a custom window, and will provide the maximize, minimize, close, drag, etc functionality. In addition to the standard "window" functions, the control will should also contain the main "menu" of the application - essentially a collection of buttons that each associate with a command and/or viewmodel - these buttons are custom controls as well (derived from radio buttons).
Essentially, my goal is to be able to add this menu control and it's buttons via XAML in a manner like this (this is pseudocode, to be clear):
<MenuControl Title="ApplicationTitle>
<MenuControl.MenuButtons>
<MenuButton Content="Button1" Command="Command1"/>
<MenuButton Content="Button2" Command="Command2"/>
</MenuControl.MenuButtons>
</MenuControl>
I've gotten to the point where I can get this working correctly for only ONE button. Once I add a second button, I get a "Specified argument was out of the range of the valid values" from my XAML.
Here is the code-behind related to the menu on my custom control:
private static readonly DependencyProperty MenuProperty = DependencyProperty.Register("Menu", typeof(ObservableCollection<NavigationButton>), typeof(CCTNavigationHeader), new FrameworkPropertyMetadata(new ObservableCollection<NavigationButton>()));
public ObservableCollection<NavigationButton> Menu
{
get
{
return (ObservableCollection<NavigationButton>)GetValue(MenuProperty);
}
set
{
SetValue(MenuProperty, value);
}
}
And here is the XAML:
<ItemsControl ItemsSource="{Binding ElementName=ctlCCTNavigationHeader, Path=Menu}"/>
This is the code utilizing the control that works, with only one button:
<Controls:CCTNavigationHeader Title="Test">
<Controls:CCTNavigationHeader.Menu>
<Controls:NavigationButton Content="Test"/>
</Controls:CCTNavigationHeader.Menu>
</Controls:CCTNavigationHeader>
And this is the code using the control that chokes, once I add a second button:
<Controls:CCTNavigationHeader Title="Test">
<Controls:CCTNavigationHeader.Menu>
<Controls:NavigationButton Content="Test"/>
<Controls:NavigationButton Content="Test"/>
</Controls:CCTNavigationHeader.Menu>
</Controls:CCTNavigationHeader>
I know I must be doing something incorrectly here, but I haven't been able to find any examples of accomplishing this type of solution anywhere. Can anyone familiar with creating custom user controls in WPF point me in the right direction?
Figured this one out. I wasn't initializing the Menu collection when creating the control. The following code fixed it:
public CCTNavigationHeader()
{
InitializeComponent();
Menu = new ObservableCollection<NavigationButton>(); //this line
}

Using MVVM show new window and get updates data

I'm working on a WPF MVVM application. I'm showing some data in a datagrid. I've two buttons to Add and Edit the selected record. I've data in ViewModel and I've to show another window (view) and make sure that ViewModels should have no information about views.
Where should I create its view and viewmodel?
How to get the data back and update datagrid?
How can I achieve this in MVVM?
We have not yet decided to use any framework, so I've to create my own interface.
Note: This ended up being quite a long answer - please ask me if anything is unclear
The implementation of dialog windows is a contentious issue in MVVM designs, and different people use different approaches.
Like you, I've decided not to use any framework and implement most things by hand. When it comes to dialog windows, I choose to be pragmatic about my implementation of MVVM, by launching the Dialog Window from inside my ViewModel. Also, I allow each Dialog ViewModel to have a reference to the Window it is displayed in, so it can close it when appropriate (details below). This breaks some of the strict MVVM "rules", but it gets the job done.
The main downside of this is that it might break unit testing if you are testing something that goes through a dialog. However, you can go a long way without running into that problem and it has not bothered me yet.
I've built up a bit of a library of dialog ViewModels which I can easily extend. It's way too much code to post here, but I'll show you the highlights.
Base ViewModel for Dialogs
Each of my dialog windows has a ViewModel that inherits from DialogViewModelBase, which is similiar to my regular ViewModelBase in that it provides support for INotifyPropertyChanged etc. The interesting part is this public method, which I call from wherever to launch the Dialog:
/// <summary>
/// Creates window instance for this dialog viewmodel and displays it, getting the dialog result.
/// </summary>
public void ShowDialogWindow()
{
// This is a property of the DialogViewModelBase class - thus, each DialogViewModel holds a reference to its own DialogWindow:
this.DialogWindow = new Dialogs.Views.DialogWindow();
// Tell the DialogWindow to display this ViewModel:
this.DialogWindow.DataContext = this;
// Launch the Window, using a method of the Window baseclass, that only returns when the window is closed:
this.DialogWindow.ShowDialog();
}
Window launched in the above method will close when its Window.DialogResult property is set. This is why the DialogWindow is a property of the DialogViewModelBase class - when the subclassing dialog ViewModel wants to close the dialog window, it simply sets the result:
protected void CloseDialogWithResult(bool dialogWindowResult)
{
// Setting this property automatically closes the dialog window:
this.DialogWindow.DialogResult = dialogWindowResult;
}
Host Window for Dialog Views
The Dialogs.Views.DialogWindow class that the ShowDialogWindow method instantiates is defined in XAML and is a subclass of Window. It has two important features. The first is that it's primary content element is simply a ContentControl that binds to the current context. This allows me to define different Views for different subclasses of DialogViewModelBase, and the DialogWindow will host the corresponding View based on the type of the context:
<ContentControl Content="{Binding}" /> <!-- In reality this is inside a border etc but its simplified here for demonstration -->
The second important feature of the DialogWindow XAML is that it defines which dialog Views go with which dialog ViewModels. Here is a sample:
<Window.Resources>
<!-- DEFAULT ViewModel-View TEMPLATES -->
<DataTemplate DataType="{x:Type dialogs:YesNoMessageBoxDialogViewModel}">
<views:MessageBoxView />
</DataTemplate>
<DataTemplate DataType="{x:Type dialogs:ErrorDialogViewModel}">
<views:ErrorDialogView/>
</DataTemplate>
</Window.Resources>
What all this does, is that I can define dialogs as subclasses to DialogViewModelBase and implement a View for each, and then tell DialogWindow which View its ContentControl must show for which dialog ViewModel.
Launching a Dialog and getting results
Below is a sample from one of my application ViewModels, in which I launch a Dialog Window that allows the user to select an Asset Type for creation:
public void CreateNewAsset()
{
// Instantiate desired Dialog ViewModel:
Dialogs.NewAssetTypeSelectionDialogViewModel dialog = new Dialogs.NewAssetTypeSelectionDialogViewModel();
// Launch Dialog by calling method on Dialog base class:
dialog.ShowDialogWindow();
// Execution will halt here until the Dialog window closes...
// The user's selection is stored in a property on the dialog ViewModel, and can now be retrieved:
CalculatorBase.AssetTypeEnum newAssetType = dialog.AssetType;
switch (newAssetType)
{
// Do stuff based on user's selection...
}
}
PS: I should really write a blog entry about this - when I do, I will post the link here, as the blog entry will probably have more complete code samples.
It depends how you are handling the data. I will assume that changes made in the popup window can be accepted only when user clicks something like save in other case they should be discarded.
So firstly, I would suggest using MVC approach as controller is perfect for such tasks. You build viewmodels in it, assign them o views and show the views. VM's simply keeps data and commands, commands execute methods are kept in controller. In other words you have singleton class which manages your VM's and views.
You should check out Prism framework. It offers great things like view regios where you can inject different user controls on the runtime, commanding and MVC layering out of the box alongside IOC and DI patterns.

Create Silverlight custom control with code only (no xaml)

I would like to create a Silverlight custom control using C# only, without any xaml.
Here is my work so far (stripped down to the bare minimum for the question):
I tried to inherit User control as follows:
public class myControl: UserControl
{
// class code
}
And adding it to the LayoutRoot:
myControl control = new myControl();
LayoutRoot.Children.Add(control);
The control is added, but its invisible!!
How can i make it visible ? Is there something i missed ?
edit: The only visual element in my contorl is a grid with an image background
Your Usercontrol will be empty and have no visual effect until you give it a child control via it's Content property.
Well unless you put a template in place or add elements in code, UserControl is empty.
Maybe you could try inheriting from an existing control which has a template, like Button, etc and change that in code?

List<> Binding and button click using mvvm light

I am trying to use MVVM light to achieve something like this. I have the following scenario:
In my Model--I have set the properties like ActivityName,Image and there is a class constructor whose is accepting 2 parameters like name and image.
Im my DataAccess--I have set the database connection and implement the required method who will fetch the data from DB and I am storing in the List and returning the list to ViewModel.
In my ViewModel--I have created the list property who will return the list by calling the GetActivities() method that I have defined in the DataAccess.
Now my problem is I am not getting how to bind it in the View so that by clicking on a button it will display the list of activities with image. By clicking on some button a new window should open with the desired result. How to bind the above list and implement the button functionality using MVVM light.
Kindly help?
Thanks
First of all, use an ObservableCollection instead of a List as it would notify the view when property or collection changes.
Then set the DataContext of your view to the viewmodel. If you use MVVMLight View Class, then the DataContext would be automatically set. You've to just give the ViewModel Name there.
Then set the ItemsSource of the DataGrid like this <dg:DataGrid ItemsSource="{Binding YourListInViewModel}"/>
For handling click event you can use the Event-To-Command behaviour and write your logics in the Button's corresponding Command handler.
Bind to DataContext of the control

How do I databind to a ViewModel in Expression Blend?

In WPF and Silverlight you can make a view model object and set it into the DataContext of a control when the control is constructed at runtime. You can then bind properties of the visual object to properties in your DataContext.
One way to set the binding is to type the binding directly into the tag:
<TextBox Text="{Binding Path=Description}"/>
And this will bind the textbox to the Description property in the view model.
The problem with typing in the binding is that you may make a typing mistake. (And you will almost certainly make a mistake if you have hundreds of bindings to make.)
In Expression Blend there is a little white dot beside the Text property in the Properties window. This brings up a menu from which you can Create Data Binding.
How can I get my view model properties to show in the Create Data Binding dialog so that I can select them.
Will the configuration of data binding in Blend interfere with setting my view model into the DataContext at runtime?
One technique is to include the VM as a resource in your view:
<UserControl>
<UserControl.Resources>
<local:YourViewModel x:Key="ViewModel"/>
</UserControl.Resources>
</UserControl>
You can then reference that as DataContext="{StaticResource ViewModel}" elsewhere.
Can't say I like it, but I can't say I like any of the view-first idiosynchrasies that Blend imposes on your design.
I experimented with Blend to find the drag and drop approach to data binding which still lets you override your view model in code easily.
First make your view model object which implements INotifyPropertyChanged and raises the notify event in the setters. The view models can be hierarchical. For example you could have an ObservableCollection within your main view model.
In Blend open up your page or control and go to the data tab.
On the right open the menu under the "Add live data source" icon.
Pick "Define new object data source"
Select your top level view model class and confirm the dialog
In my experiments I found that it was important to bind the data source to where I wanted it first or else Blend might make a less than optimal configuration if I didn't do the next step first.
Open the Objects and Timeline window in Blend
Select the root object, for example UserControl
Open Properties and verify that the root object is selected
Find DataContext and click the square to open the menu and select DataBinding
Select the data source that was just previously created
Now that the data source has been created data binding is very easy.
put some controls on the page
open the Data window
from the DataSource for your view model drag properties onto the controls to create the bindings or set the binding from the Properties window.
Now you can create you live view model object in the constructor of the control
public MainPage()
{
// Required to initialize variables
InitializeComponent();
mVm = new MyViewModel();
this.DataContext = mVm;
}
private MyViewModel mVm;
Add any initialization to retrieve data and you are ready to go.
I have a screen cast on my blog Blendable MVVM : Introduction and Databinding which shows setting this up for Siverlight.
Essentially you create the ViewModel as the DataContext of the UserControl using the "New Object Initialiser" and then using the "Explicit Data Context" tab of the Binding dialog for the controls themselves.
Hope this helps.

Resources