Conditional Resources Creation WPF XAML Design / R

2019-03-03 01:18发布

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

bindingMCVE snoop view

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

Test application of 2 UserControls

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

simple usercontrol

  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

2条回答
萌系小妹纸
2楼-- · 2019-03-03 01:54

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: Snoop view. Only the mainwindow viewmodel instance

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

Set Design Time DataContext

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

enter image description here

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
        }
    }
}
查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-03-03 01:59

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.

查看更多
登录 后发表回答