Xaml: design a layout with dynamically visible components - wpf

In a mvvm application some areas inside a window (in reality it is a UserControl inside MainWindow) are dynamically displayed according to the user selections.
The changing blocks are inside Stackpanels, I have 4 of them and only one at a time is displayed. This is accomplished binding Visibility to a bool property and using the BooleanToVisibilityConverter.
I put all the alternate StackPanel inside parent control. It works correctly, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
How can I easily create the layout having more controls which share the same window area and are displayed one at a time ?

Setting A Design Time Only Data Context
Developing XAML in the Studio Designer can be greatly simplified by setting the Design-Time Data Context.
One implementation is based on setting a duplicate DataContext which will be ignored during the final compilation.
To implement the switching, add to the ViewModel, a property that will inform the designer whether it can be used in Development Mode or not.
I use an MVVMLight situation for this example, but for this declared instance property IsInDesignMode and static property ViewModelBase.IsInDesignModeStatic.
Example:
using System.ComponentModel;
namespace DataContextDesignTime.Example
{
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _flag;
public bool Flag
{
get => _flag;
set
{
if (!Equals(_flag, value))
{
_flag = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Flag)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NotFlag)));
}
}
}
public bool NotFlag => !Flag;
}
}
<Window x:Class="DataContextDesignTime.Example.ExamleWindow"
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:DataContextDesignTime.Example"
mc:Ignorable="d"
Title="ExamleWindow" Height="450" Width="800">
<d:Window.DataContext>
<local:MyViewModel Flag="True" NotFlag="True"/>
</d:Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<Border Background="LightBlue" Height="200"
Visibility="{Binding Flag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Border Background="LightGreen" Height="400"
Visibility="{Binding NotFlag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
In this example, you can change property values in XAML or in the Property Browser.
And you will immediately see the work of your bindings, triggers, how the display for certain data changes.
Note
This may fail on more complex VMs/packages, but in general by setting the DataContext at design time is not difficult.
I need to recompile the project to see the changes in the properties.
The XAML Designer panel has an «Enable/Disable Project Code» button.

, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
This problem is easily resolved by bringing up the Document Outline tab in visual studio. Once open, navigate to the visible tree and toggle the eyeball to visibly hide/unhide the control[s] one is not interested in; during design time only.

Related

Control Focus in dialog box with Prism 7.x and 8.x?

I need to set a TextBox being focused when the dialog box was opened. It was working with Prism 6.x; but is no longer working since 7.x and 8.x. The framework difference is that Prism 6.x uses InteractionRequest for dialog box; while Prism 7/8 uses the dialog service. Does Prism 7 and 8 also introduce some new approaches related to dialog box? Following is the code snippet of XAML setting FocusManager:
<UserControl x:Class="FeatureModule.Views.MyDialogView"
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:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
FocusManager.FocusedElement="{Binding ElementName=edit}"
mc:Ignorable="d" d:DesignHeight="140" d:DesignWidth="400">
It does not function any more. Does it related to the dialog service?
The dialog service starting from Prism 7.2.x is completely different from the legacy interaction requests in prior versions, so it is hard to tell what exactly was the breaking change. Setting the focus manager property on the root element of the UserControl instead of on the UserControl could solve this the issue, but it might not work under other circumstances, read below why.
<UserControl x:Class="FeatureModule.Views.MyDialogView" ...>
<Grid FocusManager.FocusedElement="{Binding ElementName=edit}">
<!-- ...your markup -->
</Grid>
</UserControl>
I think the issue is more general. WPF has two different focus concepts, logical and keyboard focus. What you want to set on the TextBox is keyboard focus, but the FocusManager only sets logical focus.
Logical focus pertains to the FocusManager.FocusedElement within a specific focus scope.
The important part is the relationship between logical and keyboard focus.
An element with logical focus does not necessarily have keyboard focus, but an element with keyboard focus will have logical focus.
This means, even if you set the logical focus on a different element it might not work.
Keyboard focus pertains to the element that is currently receiving keyboard input. There can be only one element with keyboard focus.
Unfortunately, the Keyboard.FocusedElement property has only a getter and is not a dependency property, so you cannot bind to it, but you can call the Keyboard.Focus method instead. In order to avoid code-behind, you can either create a small behavior or simply an attached property like this:
public static class KeyboardFocus
{
public static readonly DependencyProperty ElementProperty = DependencyProperty.RegisterAttached(
"Element", typeof(FrameworkElement), typeof(KeyboardFocus), new PropertyMetadata(null, OnElementChanged));
public static FrameworkElement GetElement(DependencyObject dependencyObject)
{
return (FrameworkElement)dependencyObject.GetValue(ElementProperty);
}
public static void SetElement(DependencyObject dependencyObject, FrameworkElement value)
{
dependencyObject.SetValue(ElementProperty, value);
}
private static void OnElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is FrameworkElement frameworkElement)
frameworkElement.Loaded += OnLoaded;
}
private static void OnLoaded(object sender, RoutedEventArgs e)
{
var frameworkElement = (FrameworkElement)sender;
frameworkElement.Loaded -= OnLoaded;
Keyboard.Focus(frameworkElement);
}
}
This attached property can be used on any FrameworkElement, not only TextBox. You can customize it to fit your needs. In XAML apply it to the root element of your UserControl, not the UserControl itself, otherwise the binding element might not be found e.g.:
<UserControl x:Class="FeatureModule.Views.MyDialogView"
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:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:FeatureModule.Views"
prism:ViewModelLocator.AutoWireViewModel="True"
FocusManager.FocusedElement="{Binding ElementName=edit}"
mc:Ignorable="d" d:DesignHeight="140" d:DesignWidth="400">
<Grid local:KeyboardFocus.Element="{Binding ElementName=edit}">
<!-- ...your markup. -->
</Grid>
</UserControl>
Alternatively, you can set the property on the TextBox itself.
<TextBox local:KeyboardFocus.Element="{Binding RelativeSource={RelativeSource Self}}"/>

WPF How to expose fields on a UserControl

I have a simple application with just a window and a user control. The user control has a list box. The user control is positioned on the Window and I want to bind the user control's listbox to an element on the window's data context.
The examples I've been able to find have CLR properties on the user control which are accessed in code not via XAML.
<Window x:Class="WpfApplication2b.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication2b="clr-namespace:WpfApplication2b" Title="MainWindow" Height="410" Width="520">
<Grid>
<WpfApplication2b:MyUserControl></WpfApplication2b:MyUserControl>
</Grid>
And here is the user control itself.
<UserControl x:Class="WpfApplication2b.MyUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="#FFD8AA13">
<ListBox Height="276" HorizontalAlignment="Left" Margin="12,12,0,0" Name="listBox1" VerticalAlignment="Top" Width="276" />
</Grid>
As you can see it's just a listbox on a different coloured background. I have no idea where to go next :)
I'm guessing that I need to add a code behind property for the list box as a dependency property?
Edit: I've added a dependencyProperty, but I don't think I've quite got the point.
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty ListBoxProperty;
static MyUserControl()
{
FrameworkPropertyMetadata md = new FrameworkPropertyMetadata();
MyUserControl.ListBoxProperty = DependencyProperty.Register("MyListBox", typeof (ListBox),
typeof (MyUserControl), md);
}
public ListBox MyListBox
{
get
{
return (ListBox) GetValue(ListBoxProperty);
}
set
{
SetValue(ListBoxProperty, value);
}
}
public MyUserControl()
{
InitializeComponent();
}
}
Your UserControl will inherit the DataContext from the Window so you can bind properties on the ListBox as though it were declared in the Window. To make the control more flexible you can declare Dependency Properties for the data items from the DataContext that you want to use (i.e. an ItemsSource collection) and pass them into the control, rather than passing the ListBox out.
I think this question/answer is almost what you're looking for. Essentially you're going to need to make a dependency property (using the AddOwner registration method) and set up the DataBinding on the ListBox's ItemsSource to hook to the Dependency Property. The example in the answer does the same thing for a ComboBox, and should be almost the same for a ListBox.
Exposing inner Control properties for binding in WPF

How to build a generic/re-usable modal dialog for WPF following MVVM

I would like to build a generic/re-usable modal dialog that I can use in our WPF (MVVM) - WCF LOB application.
I have a Views and associated ViewModels that I would like to display using dialogs. Bindings between Views and ViewModels are done using Type-targeted DataTemplates.
Here are some requirements that I have been able to draft:
I prefer this to be based on a Window instead of using Adorners and controls that act like a modal dialog.
It should get its minimum size from the content.
It should center on the owner window.
The window must not show the Minimize and Maximize buttons.
It should get its title from the content.
What is the best way to do this?
I usually deal with this by injecting this interface into the appropriate ViewModels:
public interface IWindow
{
void Close();
IWindow CreateChild(object viewModel);
void Show();
bool? ShowDialog();
}
This allows the ViewModels to spaw child windows and show them modally on modeless.
A reusable implementation of IWindow is this:
public class WindowAdapter : IWindow
{
private readonly Window wpfWindow;
public WindowAdapter(Window wpfWindow)
{
if (wpfWindow == null)
{
throw new ArgumentNullException("window");
}
this.wpfWindow = wpfWindow;
}
#region IWindow Members
public virtual void Close()
{
this.wpfWindow.Close();
}
public virtual IWindow CreateChild(object viewModel)
{
var cw = new ContentWindow();
cw.Owner = this.wpfWindow;
cw.DataContext = viewModel;
WindowAdapter.ConfigureBehavior(cw);
return new WindowAdapter(cw);
}
public virtual void Show()
{
this.wpfWindow.Show();
}
public virtual bool? ShowDialog()
{
return this.wpfWindow.ShowDialog();
}
#endregion
protected Window WpfWindow
{
get { return this.wpfWindow; }
}
private static void ConfigureBehavior(ContentWindow cw)
{
cw.WindowStartupLocation = WindowStartupLocation.CenterOwner;
cw.CommandBindings.Add(new CommandBinding(PresentationCommands.Accept, (sender, e) => cw.DialogResult = true));
}
}
You can use this Window as a reusable host window. There's no code-behind:
<Window x:Class="Ploeh.Samples.ProductManagement.WpfClient.ContentWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self="clr-namespace:Ploeh.Samples.ProductManagement.WpfClient"
xmlns:pm="clr-namespace:Ploeh.Samples.ProductManagement.PresentationLogic.Wpf;assembly=Ploeh.Samples.ProductManagement.PresentationLogic.Wpf"
Title="{Binding Path=Title}"
Height="300"
Width="300"
MinHeight="300"
MinWidth="300" >
<Window.Resources>
<DataTemplate DataType="{x:Type pm:ProductEditorViewModel}">
<self:ProductEditorControl />
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" />
</Window>
You can read more about this (as well as download the full code sample) in my book.
I'm answering my own question to help others find all answers I struggled to find in one place. What above seems like a straight forward problem, actually presents multiple problems that I hope to answer sufficiently below.
Here goes.
Your WPF window that will serve as the generic dialog can look something like this:
<Window x:Class="Example.ModalDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ex="clr-namespace:Example"
Title="{Binding Path=mDialogWindowTitle}"
ShowInTaskbar="False"
WindowStartupLocation="CenterOwner"
WindowStyle="SingleBorderWindow"
SizeToContent="WidthAndHeight"
ex:WindowCustomizer.CanMaximize="False"
ex:WindowCustomizer.CanMinimize="False"
>
<DockPanel Margin="3">
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button Content="Cancel" IsCancel="True" Margin="3"/>
<Button Content="OK" IsDefault="True" Margin="3" Click="Button_Click" />
</StackPanel>
<ContentPresenter Name="WindowContent" Content="{Binding}"/>
</DockPanel>
</Window>
Following MVVM, the right way to show a dialog is through a mediator. To use a mediator, you typically require some service locator as well. For mediator specific details, look here.
The solution I settled on involved implementing an IDialogService interface that is resolved through a simple static ServiceLocator. This excellent codeproject article has the details on that. Take note of this message in the article forum. This solution also solves the problem of discovering the owner window via the ViewModel instance.
Using this interface, you can call IDialogService.ShowDialog(ownerViewModel, dialogViewModel). For now, I'm calling this from the owner ViewModel, meaning I have hard references between my ViewModels. If you use aggregated events, you will probably call this from a conductor.
Setting the minimum size on the View that will eventually be displayed in the dialog doesn't automatically set the minimum size of the dialog. Also, since the logical tree in the dialog contains the ViewModel, you can't just bind to the WindowContent element's properties. This question has an answer with my solution.
The answer I mention above also includes code that centers the window on the owner.
Finally, disabling the minimize and maximize buttons is something WPF can't natively do. The most elegant solution IMHO is using this.

WPF: Composite Application with Page Navigation

I am currently writing an application to which the composite methodology fits like a glove.... almost!
I also need a way to navigate between views, including maintaining a journal for navigation backward and forward.
What is the best way to combine these two methodologies, on one hand the single Window based CAG shell with its UserControl derived views, and on the other hand the convenient NavigationWindow shell with its Page derived views and journal?
Thanks!
You can display anything in a NavigationWindow, not just Pages. A simple way to make it work is to define in the NavigationWindow's resources a DataTemplate for each ViewModel you want to display. Bind the Content property of the NavigationWindow to a property of your main ViewModel, and you're done : changing that property will update the NavigationWindow content, and the appropriate DataTemplate will be picked automatically
UPDATE
I just looked at the code of a project of mine where I used a NavigationWindow. Actually I was mistaken, it doesn't work by binding the Content (or maybe it works, but that's not what I did). Instead I created a INavigationService interface, implemented by my App class, which handles the navigation by calling the NavigationWindow.Navigate method. That way, the navigation history is maintained by the NavigationWindow.
Here's an extract from my project
MainWindow.xaml :
<NavigationWindow x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyApp.ViewModel"
xmlns:view="clr-namespace:MyApp.View"
Title="{Binding Content.DisplayName, RelativeSource={RelativeSource Self}, FallbackValue=The Title}"
Height="600" Width="800">
<NavigationWindow.Resources>
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<view:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<view:CustomerView />
</DataTemplate>
</NavigationWindow.Resources>
</NavigationWindow>
App.xaml.cs :
...
private void Application_Startup(object sender, StartupEventArgs e)
{
LoadConfig();
MyApp.MainWindow window = new MainWindow();
INavigationService navigationService = this;
HomeViewModel viewModel = new HomeViewModel(navigationService);
this.MainWindow = window;
window.Navigate(viewModel);
window.Show();
}
When I need to navigate to another view, I just call the Navigate method with the ViewModel as a parameter, and WPF automatically picks the appropriate DataTemplate from the resources.

Handle exceptions with WPF and MVVM

I am attempting to build an application using WPF and the MVVM pattern. I have my Views being populated from my ViewModel purely through databinding. I want to have a central place to handle all exceptions which occur in my application so I can notify the user and log the error appropriately.
I know about Dispatcher.UnhandledException but this does not do the job as exception that occur during databinding are logged to the output windows. Because my View is databound to my ViewModel the entire application is pretty much controlled via databinding so I have no way to log my errors.
Is there a way to generically handle the exceptions raised during databinding, without having to put try blocks around all my ViewModel public's?
Example View:
<Window x:Class="Test.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestView" Height="600" Width="800"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<StackPanel VerticalAlignment="Center">
<Label Visibility="{Binding DisplayLabel, Converter={StaticResource BooleanToVisibilityConverter}}">My Label</Label>
</StackPanel>
</Window>
The ViewModel:
public class TestViewModel
{
public bool DisplayLabel
{
get { throw new NotImplementedException(); }
}
}
It is an internal application so I do not want to use Wer as I have seen previously recommended.
The Binding implementation is designed to be fault tolerant and so it catches all the exceptions. What you could do is to activate the following properties in your bindings:
ValidatesOnExceptions = true
NotifyOnValidationError = true
See also the MSDN.
This causes to raise the attached Error property on the bound control.
However, this infrastructure is designed for validating the user input and show validation messages. I’m not sure if this is what you are doing.

Resources