Home > Back-end >  WPF UserControl Styling
WPF UserControl Styling

Time:12-01

I'm quite 'green' into WPF and I appreciate if you could share some starting point example or help me fixing my own code. I have tree UserControl (Component, ComponentTop, ComponentBottom) that share the same ViewModel class 'ComponentViewModel'. Instead of using this tree UserControl I would like to use just 'Component' to host the Style and DataContext (ComponentViewModel) and create 3 styles (Base,Top and Bottom) and then I just need to set Component.Style to alternate component visualization.

I've try to declare a style in a resource dictionary but the binding doesn't work. And from the UserControl I can set the style "Style={StaticResource Base}" but after building the project I get error code saying 'Resource not found'.

The Style:

<Style x:Key="Base" TargetType="UserControl">

    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Border BorderThickness="0.5" BorderBrush="Gray">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition x:Name="Head" Height="Auto"/>
                            <RowDefinition x:Name="Content" Height="Auto"/>
                        </Grid.RowDefinitions>
                        <Border  Grid.Column="0"  Margin="1"  BorderThickness="0.25" BorderBrush="Black" Background="{Binding StatusColor}">

                            <Grid HorizontalAlignment="Stretch">
                                <TextBlock Margin="1,0,1,0" Text="{Binding Name, FallbackValue=######}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                                <Image   HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,0,1" Width="10" Height="10"  Source="{Binding PriorityImage}" Visibility="{Binding PriorityImageVisibility}"></Image>
                            </Grid>
                        </Border>

                        <Grid Grid.Row="1">

                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="auto"/>
                                <ColumnDefinition Width="auto"/>
                            </Grid.ColumnDefinitions>

                            <layout:TagsContainer        Margin="2,0,0,0"  Grid.Column="0"  VerticalAlignment="Top" HorizontalAlignment="Left"    DataContext="{Binding TagsContainerDataContext}"/>

                            <layout:ControlTagsContainer Margin="5,0,2,0"  Grid.Column="1"  VerticalAlignment="Top" HorizontalAlignment="Left"    DataContext="{Binding ControlTagsContainerDataContext}"/>

                        </Grid>

                        <Image  Grid.Row="1"  Grid.RowSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="25" Height="25" MaxHeight="35" MaxWidth="35" Source="{Binding StatusImage}" Visibility="{Binding StatusImageVisibility}" ></Image>

                    </Grid>
                </Border>
            </DataTemplate>
            
        </Setter.Value>
        
    </Setter>

</Style>

The UserControl:

<UserControl
             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:ProjectX.UI.Layout"
             xmlns:ViewModels="clr-namespace:ProjectX.UI.Layout.ViewModels" 
             x:Class="ProjectX.UI.Layout.Component"
             mc:Ignorable="d" Cursor="" x:Name="Root" Height="auto" MinHeight="10" MinWidth="10" FontSize="10" Width="auto" Style="{ StaticResource Base }" >

<UserControl.DataContext>
    <ViewModels:ComponentViewModel/>
</UserControl.DataContext>

<UserControl.Resources>


    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source= "Components.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

</UserControl.Resources>

TheViewModel:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ProjectX.Model.Component;
using DevExpress.Mvvm;
using System.Windows.Media;
using ProjectX.Model.Tag;
using ProjectX.UI.Tag;
using AppContext = ProjectX.Model.Tag.AppContext;
using System.Windows;
using System.Windows.Media.Imaging;
using ProjectX.Model.Component.Components;

namespace ProjectX.UI.Layout.ViewModels
{
    public class ComponentViewModel : ViewModelBase
    {
        private ComponentBase DataModel   = new ComponentBase();

        public string? Name
        {
            get { return GetValue<string>(); }
            private set { SetValue(value); }
        }

        public Brush StatusColor
        {
            get { return GetValue<Brush>(); }
            private set { SetValue(value); }
        }

        public ImageSource StatusImage
        {
            get { return GetValue<ImageSource>(); }
            private set { SetValue(value); }
        }

        public Visibility StatusImageVisibility
        {
            get { return GetValue<Visibility>(); }
            private set { SetValue(value); }
        }

        public ImageSource PriorityImage
        {
            get { return GetValue<ImageSource>(); }
            private set { SetValue(value); }
        }

        public Visibility PriorityImageVisibility
        {
            get { return GetValue<Visibility>(); }
            private set { SetValue(value); }
        }


        public Visibility SHControlsVisibility
        {
            get { return GetValue<Visibility>(); }
            private set { SetValue(value); }
        }

       public TagsContainerViewModel TagsContainerDataContext
        {
            get { return GetValue<TagsContainerViewModel>(); }
            private set { SetValue(value); }
        }

        public ControlTagsContainerViewModel ControlTagsContainerDataContext
        {
            get { return GetValue<ControlTagsContainerViewModel>();}

            private set { SetValue(value); }
        }



        private List<RuntimeTagViewModel>? Tags = null;        
        private List<RuntimeTagViewModel>? ControlTags = null; 



        public ComponentViewModel()
        {
            Name = "COMPONENT X";
            TagsContainerDataContext = new TagsContainerViewModel();
            ControlTagsContainerDataContext = new ControlTagsContainerViewModel();
            Init();
        }

        public ComponentViewModel(ComponentBase datamodel)
        {
            DataModel   = datamodel;
            Name        = datamodel.Label;
            Tags = DataModel.Tags.Where(x => x.AppContext == AppContext.Layout && x.Scope == Scope.User).Select(x => new RuntimeTagViewModel(x)).ToList();
            ControlTags = DataModel.Tags.Where(x => x.AppContext == AppContext.Control && x.Scope == Scope.User).Select(x => new RuntimeTagViewModel(x)).ToList();
            TagsContainerDataContext = new TagsContainerViewModel(Tags);
            ControlTagsContainerDataContext = new ControlTagsContainerViewModel(ControlTags);

            Init();
        }



        private void Init()
        {

            StatusColor = Brushes.Gray;
            SetStatusImage(StatusEnum.Warning);

            SHControlsVisibility   = Visibility.Collapsed;
            PriorityImageVisibility = Visibility.Collapsed;

            if (DataModel.Interface == nameof(IDamper))
            {
                PriorityImage = Global.Resources.Images.Priority;
                PriorityImageVisibility = Visibility.Visible;
            }

            if (DataModel.Interface == nameof(ISystemHandler))
            {
                SHControlsVisibility = Visibility.Visible;
            }

        }

        public void SetStatusImage(StatusEnum status = StatusEnum.None)
        {
            switch (status)
            {
                case StatusEnum.None:
                    StatusImage = Global.Resources.Images.Warning;
                    break;
                case StatusEnum.Error:
                    StatusImage = Global.Resources.Images.Error;
                    break;
                case StatusEnum.Warning:
                    StatusImage = Global.Resources.Images.Warning;
                    break;
                case StatusEnum.Info:
                    StatusImage = Global.Resources.Images.Info;
                    break;
                default:
                    throw new NotImplementedException();
            }


            if (status != StatusEnum.None)
            {
                StatusImageVisibility = Visibility.Visible;
            }
            else
            {
                StatusImageVisibility = Visibility.Hidden;
            }
        }

    }
}

Thank you!

CodePudding user response:

If I understood you correctly, your XAML in the Development mode does not issue errors and warnings and works as you expect. And at runtime, an error occurs due to finding the style you need in the "Components.xaml" file.

The StaticResource is evaluated at the time the element is created. And the Resources collection is filled in and added later. Due to the peculiarities of the designer's work, this error is not always displayed in this mode.
In your case, it will be enough to replace StaticResource with DynamicResourse.

CodePudding user response:

After some time and research I fix the binding problem by using the following sintax:

Background="{Binding DataContext.StatusColor, RelativeSource={RelativeSource AncestorType=layout:Component}}

Instead of original one:

Background="{Binding StatusColor}"
  • Related