Conditional Resources Creation WPF XAML Design / R

2019-03-03 00:58发布

问题:

Following a first question on WPF Cascading Binding,

I remarked that I had more Resources than desired defined in both the MainWindow and the UserControls:

This is well seen in Snoop

  • the MainWindow XAML has an "instance" of a MainWindow ViewModel that paints the 2 usercontrol in green and blue

  • additionnaly there are 2 instances of usercontrol viewmodels for each of the 2 UserControls that would paint the inner circle in red

  1. My first Intention in the Usercontrol was to be able to see "live" how my control would look like ( I painted it red to distinguish this "model" from the green and blue circles in the Main Window)
  2. My Resources are very cheap to create, so no matter if I have 2 extra resources that are still living in my application, but I would anticipate the time where my Resources will be more expensive...

Finally my question is :

How can I conditionnaly let Resources be created in "low level" control ( so that I can have a preview in the view of the control ) but prevent this creation when running the full application ( or the view of the Main Window ) because I will bind to the Resources of this upper level.

Many thanks in advance.

Best Regards.

NGI

回答1:

I prepared simplified way out displaying one data during design time and latter during run time. Hopefully you will find it useful and adjust to your case.

XAML:

<Window.Resources>
    <DataTemplate x:Key="DesignModeDataTemplate">
        <TextBlock Text="Design mode"/>
    </DataTemplate>
    <DataTemplate x:Key="RunTimeDataTemplate">
        <TextBlock Text="Run Time"/>
    </DataTemplate>
    <local:IsInDesignModeConverter x:Key="IsInDesignModeConverter"/>
</Window.Resources>
<ContentControl>
    <ContentControl.ContentTemplate>
        <MultiBinding Converter="{StaticResource IsInDesignModeConverter}">
            <Binding Source="{StaticResource DesignModeDataTemplate}"/>
            <Binding Source="{StaticResource RunTimeDataTemplate}"/>
        </MultiBinding>
    </ContentControl.ContentTemplate>
</ContentControl>

Converter:

 class IsInDesignModeConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((bool)(DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue))
            return values[0];
        return values[1];
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

As a result it display Design mode text during design time and Run time when running. In your case instead of TextBlock you can insert already defined resources.



回答2:

I will finally try to answer my own question

Result is as awaited no "duplicate resources" in the lower level ViewModel instances The result in snoop:

The XAML code

<UserControl x:Class="BindingMCVE.UserControl1"
         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:BindingMCVE"
         d:DataContext="{d:DesignInstance {x:Type vm_nmspc:my_usercontrol_vm}, IsDesignTimeCreatable=True}"
         mc:Ignorable="d" 
         d:DesignHeight="100" d:DesignWidth="100"
         xmlns:vm_nmspc="clr-namespace:BindingMCVE.ViewModel"               
 >

<UserControl.Resources>
    <!--No resources created in XAML-->
</UserControl.Resources>


        <Grid>
    <StackPanel  Height="100"  Width="100">
        <Ellipse  Fill="{Binding my_color}" Height="50" Width="50" />
    </StackPanel>

</Grid>

Note the new line:

d:DataContext="{d:DesignInstance {x:Type vm_nmspc:my_usercontrol_vm}, IsDesignTimeCreatable=True}"

And there is absolutely no other modifications !!

The trick is to use the IDE to support you ( if you are unaware of the syntax that will result in the XAML ).

I will try to illustrate what I did.

Choose your object, and in the format menu choose Set Design Time DataContext

Then choose your DesignInstance ( here the ViewModel my_usercontrol_vm) and do not forget to tick IsDesignTimeCreatable

This created the magic line in the Ellipse declaration but I moved it on the top of the XAML.

So this answered my question. It is possible with no deep knowledge on WPF/XAML to make all your subviews (controls) "rendered" without the additional cost of wasted resources of viewmodels

To give echo to what Maximus gave in the first answer, I found also another post (will try to find it again and quote it in a next edit) using directly the function DesignerProperties.GetIsInDesignMode( )

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {

        InitializeComponent();

        if (  DesignerProperties.GetIsInDesignMode(this))
        {
            //Do what you want here for design time specific actions
        }
    }
}