Swapping ContentPresenter content in custom control - c#

I have 2 ContentPresenter fixedContentPresenter and resizableContentPresenter and obviously a Content in Generic.xaml. How can i set the Content to be only in fixedContentPresenter when FixedContent = true; and Content to be only in resizableContentPresenter when FixedContent = false;
I tried changing the content in the code but the content is not showing.
Xaml:
<Grid>
<ContentPresenter x:Name="fixedContent"/>
<Grid>
<ContentPresenter x:Name="resizableContent"/>
</Grid>
</Grid>

Use an implicit style on the content presenters to either hide or show a specific presenter depending on the value of FixedContent
<Style TargetType={x:Type ContentPresenter } x:Key="HideOrShow">
<Style.Triggers>
<DataTrigger Binding="{Binding FixedContent}" Value="False">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>

You should use ContentControl here instead of ContentPresenter.
When FixedContent values changes, Content becomes null, so no Animation related side-effects.
<Grid Background="Purple">
<ContentControl x:Name="fixedContent" Margin="0,75,0,0">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding FixedContent, Mode=OneWay}" Value="false">
<DataTrigger.Setters>
<Setter Property="Content" Value="{StaticResource ContentKey}"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<Grid Background="Red" Margin="0,54,0,0">
<ContentControl x:Name="resizableContent" Margin="0,75,0,0">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding FixedContent, Mode=OneWay}" Value="true">
<DataTrigger.Setters>
<Setter Property="Content" Value="{StaticResource ContentKey}"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</Grid>

After a day of struggle, I finally found a better answer than that proposed by #OmegaMan, let's hope it help others. Here's the new XAML (added Content="{x:Null}" to both ContentPresenter):
<Grid>
<ContentPresenter x:Name="fixedContent" Content="{x:Null}"/>
<Grid>
<ContentPresenter x:Name="resizableContent" Content="{x:Null}"/>
</Grid>
</Grid>
And the FixedContent logic:
public bool FixedContent
{
get { return (bool)GetValue(FixedContentProperty); }
set
{
SetValue(FixedContentProperty, value);
if (value) // Is Fixed
{
ResizableContentPresenter.Content = null;
FixedContentPresenter.Content = Content;
}
else
{
FixedContentPresenter.Content = null;
ResizableContentPresenter.Content = Content;
}
}
}

Related

WPF Custom Base Window Class and Style

I have a an app that will open many windows, and I want all windows to look the same. I am overriding the default Windows window chrome style and making my own, so any new window that is opened (excluding messageboxes) should have the same window style. However, no matter what I seem to try it does not work. I can get it to work with one window, but when I want to make it a global style it always crashes or simply doesn't work as it should.
Here is my code:
WindowBaseStyle.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject.Styles"
xmlns:views="clr-namespace:Myproject.Views">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="GlobalStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type views:WindowBase}" BasedOn="{StaticResource {x:Type Window}}">
<Setter Property="AllowsTransparency" Value="False" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="WindowState" Value="Normal" />
<Setter Property="WindowStyle" Value="SingleBorderWindow" />
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="30"
UseAeroCaptionButtons="False"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type views:WindowBase}">
<Border BorderBrush="Blue" BorderThickness="1" SnapsToDevicePixels="True">
<DockPanel Background="White" LastChildFill="True" >
<Grid Background="Blue" DockPanel.Dock="Top">
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Center">
<Button Name="PART_SystemMenuButton" Command="{Binding MenuCommand}" Style="{DynamicResource SystemIconButton}">
<Image Height="16" Width="16" Source="/Resources/icon.png" Stretch="Fill"/>
</Button>
<Viewbox Height="16" HorizontalAlignment="Stretch" Margin="14,2,0,0" >
<TextBlock FontSize="12" Foreground="White" Text="{Binding Title,RelativeSource={RelativeSource FindAncestor,AncestorType=Window}}" />
</Viewbox>
</StackPanel>
<StackPanel Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
<Button x:Name="PART_MinimizeButton" Command="{Binding MinimizeCommand}" Height="30" Margin="0,0,0,0" ToolTip="Minimize" Width="45">
<Image Source="/Resources/minimize.png" Stretch="None" />
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="#0079CB" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#64AEEC"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button x:Name="PART_MaximizeButton" Command="{Binding MaximizeCommand}" Height="30" Margin="0,0,0,0" Width="45">
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=WindowState}" Value="Normal">
<Setter Property="Source" Value="/Resources/maximize.png" />
<Setter Property="Stretch" Value="None" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=WindowState}" Value="Maximized">
<Setter Property="Source" Value="/Resources/unmaximize.png" />
<Setter Property="Stretch" Value="None" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="#0079CB" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#64AEEC"/>
</Trigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=WindowState}" Value="Normal">
<Setter Property="ToolTip" Value="Maximize" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=WindowState}" Value="Maximized">
<Setter Property="ToolTip" Value="Restore Down" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button x:Name="PART_CloseButton" Command="{Binding CloseCommand}" Height="30" Margin="0,0,0,0" ToolTip="Close" Width="45" >
<Image Source="/Resources/close.png" Stretch="None" />
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="#0079CB" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</StackPanel>
</Grid>
<!-- this ContentPresenter automatically binds to the content of the window -->
<ContentPresenter />
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
WindowBase.cs
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
namespace MyProject.Views
{
[TemplatePart(Name = "PART_MinimizeButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_MaximizeButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_CloseButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_SystemMenuButton", Type = typeof(Button))]
public class WindowBase: Window
{
static WindowBase()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomWindow), new FrameworkPropertyMetadata(typeof(CustomWindow)));
}
public WindowBase()
{
Loaded += (sender, evnt) =>
{
var MinimizeButton = (Button)Template.FindName("PART_MinimizeButton", this);
var MaximizeButton = (Button)Template.FindName("PART_MaximizeButton", this);
var CloseButton = (Button)Template.FindName("PART_CloseButton", this);
var SystemMenuButton = (Button)Template.FindName("PART_SystemMenuButton", this);
MinimizeButton.Click += (s, e) => WindowState = WindowState.Minimized;
MaximizeButton.Click += (s, e) => WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
CloseButton.Click += (s, e) => Close();
SystemMenuButton.Click += (s, e) => SystemCommands.ShowSystemMenu(this, GetMousePosition());
};
}
}
}
Window1.xaml
<local:WindowBase x:Class="MyProject.Views.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject.Views"
Height="750"
Width="1125">
<Grid>
</Grid>
</local:WindowBase>
Window1.xaml.cs
using System.Windows;
namespace MyProject.Views
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1: WindowBase
{
public Window1()
{
InitializeComponent();
}
}
}
I am overall following the MVVM pattern, and for the most part from all the articles and videos I have looked at online, they all follow this basic approach and they say it all works, but I can't seem to get it to work.
An additional note is that whenever I add my custom window control to the Window1.xaml file, it breaks the designer and says it is "Invalid Markup"
Also note I added my "WindowBaseStyle" resource dictionary to the App.xaml file as a merged resource dictionary.
Any help is greatly appreciated!! Thanks
Ok, as we discussed in the comments, it seems like the fastest solution to the problem you described is to use a StaticResource to get the window's style from a resource dictionary (or create an implicit style for windows.) I questioned the role of CustomWindow because I thought that might be causing problems with your default style override. (Remember: if you go the lookless control route and try to use the DefaultStyleKeyProperty override, you have to do this on every subclass of that control.)
However, I think doing something like this will get you reusable plumbing for multiple windows driven by viewmodels...
PopupHost
A class that would derive from your customized window. This code provides the following behaviors:
Allows the viewmodel to mark itself as having served its purpose, causing the window to close.
Allows attached properties to be specified by individual views that can affect the way the window appears on-screen, e.g. the window title.
Can be extended to notify presented items that the user has tried closing the window, allowing interception/cancellation or cleanup actions to be performed.
Code:
public class PopupHost : Window
{
private readonly AwaitableViewModelBase _viewModel;
public PopupHost(Window owner, AwaitableViewModelBase viewModel, string dataTemplateKey = null)
{
Owner = owner;
_viewModel = viewModel;
// Wrap the content in another presenter -- makes it a little easier to get to in order to look for attached properties.
var contentPresenter = new ContentPresenter
{
Content = viewModel
};
if (!string.IsNullOrWhiteSpace(dataTemplateKey))
contentPresenter.ContentTemplate = (DataTemplate) FindResource(dataTemplateKey);
Content = contentPresenter;
Task.Run(async () =>
{
await viewModel.Task;
Dispatcher.Invoke(Close);
});
Closed += ClosedHandler;
ApplyTemplate();
// Grab attached property values from the user control (or whatever element... you just need to find the descendant)
var contentElement = FindDescendantWithNonDefaultPropertyValue(contentPresenter, PopupWindowProperties.TitleProperty);
if (contentElement != null)
{
var binding = new Binding { Source = contentElement, Path = new PropertyPath(PopupWindowProperties.TitleProperty) };
SetBinding(TitleProperty, binding);
}
}
private void ClosedHandler(object sender, EventArgs args)
{
_viewModel?.Cancel();
Closed -= ClosedHandler;
}
private static Visual FindDescendant(Visual element, Predicate<Visual> predicate)
{
if (element == null)
return null;
if (predicate(element))
return element;
Visual foundElement = null;
(element as FrameworkElement)?.ApplyTemplate();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
var visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = FindDescendant(visual, predicate);
if (foundElement != null)
break;
}
return foundElement;
}
private static Visual FindDescendantWithNonDefaultPropertyValue(Visual element, DependencyProperty dp)
{
return FindDescendant(element, e => !(dp.GetMetadata(e).DefaultValue ?? new object()).Equals(e.GetValue(dp)));
}
}
PopupWindowProperties
Just a dumb object containing solely attached properties so your views can convey some information to the window.
public static class PopupWindowProperties
{
public static readonly DependencyProperty TitleProperty = DependencyProperty.RegisterAttached("Title", typeof(string), typeof(PopupWindowProperties), new FrameworkPropertyMetadata(string.Empty));
public static void SetTitle(UIElement element, string value) => element.SetValue(TitleProperty, value);
public static string GetTitle(UIElement element) => element.GetValue(TitleProperty) as string;
}
AwaitableViewModelBase
A simple abstract viewmodel which has a TaskCompletionSource. This allows the popup window and the viewmodel to coordinate closing.
public abstract class AwaitableViewModelBase : ViewModelBase
{
protected TaskCompletionSource<bool> TaskCompletionSource { get; set; }
public Task<bool> Task => TaskCompletionSource?.Task;
public void RegisterTaskCompletionSource(TaskCompletionSource<bool> tcs)
{
var current = TaskCompletionSource;
if (current != null && current.Task.Status == TaskStatus.Running)
throw new InvalidOperationException();
TaskCompletionSource = tcs;
}
public virtual void Cancel() => SetResult(false);
protected void SetResult(bool result) => TaskCompletionSource?.TrySetResult(result);
}
WindowService
Last but not least, the simple service that can present the requested view and viewmodel. You can use implicit DataTemplates for your viewmodels, or provide the specific x:Key value of the template you wish to use. Note that the await doesn't really do anything here because ShowDialog blocks. We return the bool since it can be used to easily identify if the user hit OK or Cancel on a modal.
public class WindowService
{
public async Task<bool> ShowModalAsync(AwaitableViewModelBase viewModel, string dataTemplateKey = null)
{
var tcs = new TaskCompletionSource<bool>();
viewModel.RegisterTaskCompletionSource(tcs);
Application.Current.Dispatcher.Invoke(() =>
{
var currentWindow = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive) ?? Application.Current.MainWindow;
var window = new PopupHost(currentWindow, viewModel, dataTemplateKey);
window.ShowDialog();
});
return await viewModel.Task;
}
}

XAML How apply styles according the value of paramter

The goal is to check the state of parameters, the state of each parameter can take the enum value (lock, unlock, or valueIncorrect). The display
will be different according to the state of the parameter(e.g, lock rectangle will be red, unlock the text will be bold and vallueIncorrect will be gray).
In main class we have different parameters VarA, VarB and VarC for example. The state of each parameter can be different.
enum State{lock, unlock, valueIncorrect};
State VarA = lock
State VarB = unlock
State VarC = lock
before I was this style and a rectangle based on this style and When I pressed a button , the style will change, but the reaction will be the same on all parameter
<Style x:Key="DisplayLockGroup" TargetType="GroupBox">
<Setter Property="BorderThickness" Value="2"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Source={x:Static local:LockMgt.Instance}, Path=isLocked}" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={x:Static local:LockMgt.Instance}, Path=isLocked}" Value="false">
<Setter Property="BorderBrush" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
<Rectangle x:Name="r_LockEcuTypes" Grid.Row="5" Grid.RowSpan="3" Grid.Column ="1" Grid.ColumnSpan="7" Style="{DynamicResource DisplayLockRectangleGroup}" />
Now i would like to change this behavior, I have different parameter and I would like to individually check the state of this parameter and apply one style according to the value, but i don't know How I can do it.
E.g for one parameter:
State VarA=lock => i would like to apply the style1
State VarB=unlock => i would like to apply the style2
State VarC=lock => i would like to apply the style1 but if the value change from lock to unlock, I would like to apply the style2
I don't know how create the XAML to display correctly what is expected.
As commented, depending on your actual needs, you have several options.
Converter from State to some property value
Change the properties via Style.Triggers (similar to your current approach)
Wrap your control and exchange inner styles via ControlTemplate.Triggers on the wrapper control
The following example is presenting three rectangles (rect1, rect2 and rect3 representing the approach with the same number) and a button that is responsible for changing the State of the ViewModel object in order to show how each rectangle reacts.
The XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525"
Loaded="Window_Loaded">
<Window.Resources>
<!-- Used for 1. -->
<local:StateToBorderBrushConverter x:Key="stateConverter"/>
<!-- Used for 2. -->
<Style x:Key="DisplayLockGroup" TargetType="Rectangle">
<Setter Property="StrokeThickness" Value="2"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Data1State}" Value="{x:Static local:State.locked}">
<Setter Property="Stroke" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Data1State}" Value="{x:Static local:State.unlock}">
<Setter Property="Stroke" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
<!-- Used for 3. -->
<Style x:Key="DefaultRectangleStyle" TargetType="Rectangle">
<Setter Property="StrokeThickness" Value="2"/>
</Style>
<Style x:Key="LockedRectangleStyle" TargetType="Rectangle">
<Setter Property="StrokeThickness" Value="2"/>
<Setter Property="Stroke" Value="Red" />
</Style>
<Style x:Key="UnlockedRectangleStyle" TargetType="Rectangle">
<Setter Property="StrokeThickness" Value="2"/>
<Setter Property="Stroke" Value="Green" />
</Style>
</Window.Resources>
<Grid x:Name="grid1">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Rectangle x:Name="rect1" Grid.Row="0" Margin="5" StrokeThickness="2" Stroke="{Binding Data1State,Converter={StaticResource stateConverter}}" />
<Rectangle x:Name="rect2" Grid.Row="1" Margin="5" Style="{StaticResource DisplayLockGroup}" />
<ContentControl Margin="5" Grid.Row="2">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Rectangle x:Name="rect3" Style="{StaticResource DefaultRectangleStyle}" />
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Data1State}" Value="{x:Static local:State.locked}">
<Setter TargetName="rect3" Property="Style" Value="{StaticResource LockedRectangleStyle}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Data1State}" Value="{x:Static local:State.unlock}">
<Setter TargetName="rect3" Property="Style" Value="{StaticResource UnlockedRectangleStyle}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
<Button x:Name="ChangeStateButton" Grid.Row="3" Margin="5" VerticalAlignment="Center" Content="Change State" Click="ChangeStateButton_Click"/>
</Grid>
</Window>
The Code
namespace WpfApplication2
{
public enum State
{
locked,
unlock,
valueIncorrect
}
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string prop = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
}
public class MyData : BaseViewModel
{
private State _Data1State;
public State Data1State
{
get { return _Data1State; }
set
{
if (_Data1State != value)
{
_Data1State = value;
NotifyPropertyChanged();
}
}
}
}
public class StateToBorderBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is State)
{
var s = (State)value;
switch (s)
{
case State.locked:
return new SolidColorBrush(Colors.Red);
case State.unlock:
return new SolidColorBrush(Colors.Green);
case State.valueIncorrect:
return new SolidColorBrush(Colors.White);
default:
break;
}
}
return new SolidColorBrush(Colors.Transparent);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private MyData ContextData;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ContextData = new MyData { Data1State = State.locked };
grid1.DataContext = ContextData;
}
private void ChangeStateButton_Click(object sender, RoutedEventArgs e)
{
switch (ContextData.Data1State)
{
case State.locked:
ContextData.Data1State = State.unlock;
break;
case State.unlock:
ContextData.Data1State = State.valueIncorrect;
break;
case State.valueIncorrect:
ContextData.Data1State = State.locked;
break;
default:
ContextData.Data1State = State.locked;
break;
}
}
}
}
Pros and cons:
The converter approach is useful, if the same property type is targeted in many different control types. The converter is returning a Brush and it doesn't care whether that brush will be used in a Rectangle.Stroke, a Border.BorderBrush or some other context.
The style trigger approach is less flexible regarding the targeted control type, but it is easier to maintain changes of more than a single property based on the status.
The control template trigger approach is useful in some advanced scenarios. It allows separate definition of the styles for each state. However, I'd only recommend it if you actually derive your own custom control with additional functionality, not as an ad-hoc control template just for switching styles.
You can apply an enum to color converter in the XAML binding
Background="{Binding Source=StateValue,
Converter={StaticResource stateValueColorConverter}}"
You'll easily find the detailed documentation: this is to give you the main idea of the usage.
Thanks to grek40, it is exactly what I want. I have try the option 3 and 2 and it works fine, I have use the case 2, it is most easily to implement.
I have use the same behavior for add an image on lock and unlock state.
In the Xaml.cs file, How I can access to parameter rect3 on your proposal, when I try it I have compilation error "the name "rect3" does not exist in the current view, I have written
XAML.CS :
rect3.Visibility = Visibility.Collapsed;
XAML :
<ContentControl Grid.RowSpan="3" Grid.ColumnSpan="3" KeyboardNavigation.IsTabStop="False">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Rectangle x:Name="rect3" Style="{StaticResource DefaultRectangleStyle}" />
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Data1State}" Value="{x:Static local:State.locked}">
<Setter TargetName="rect3" Property="Style" Value="{StaticResource LockedRectangleStyle}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Data1State}" Value="{x:Static local:State.unlock}">
<Setter TargetName="rect3" Property="Style" Value="{StaticResource UnlockedRectangleStyle}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>

XAML trigger not triggering

do you see anything wrong with this trigger?
<Border Margin="2" BorderBrush="#6593CF" BorderThickness="1.5" >
<Border.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsCreating, UpdateSourceTrigger=PropertyChanged}" Value="true">
<Setter Property="Border.BorderBrush" Value="Lime"/>
<Setter Property="Border.BorderThickness" Value="3" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
It is bound to this property:
private bool _IsCreating = false;
public bool IsCreating
{
get
{
return _IsCreating;
}
set
{
if (_IsCreating == value)
{
return;
}
_IsCreating = value;
RaisePropertyChanged("IsCreating");
}
}
When I set IsCreating = true, the style does not update.
You have set BorderBrush and BorderThinkness inline on your border. Inline declaration has quite hight precedence and override your trigger. Remove your inline declaration from border and move it to trigger:
<Border Margin="2">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=DataContext.IsCreating, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Value="true">
<Setter Property="BorderBrush" Value="Lime" />
<Setter Property="BorderThickness" Value="3" />
</DataTrigger>
<DataTrigger
Binding="{Binding Path=DataContext.IsCreating, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Value="false">
<Setter Property="BorderBrush" Value="#6593CF" />
<Setter Property="BorderThickness" Value="1.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<CheckBox IsChecked="{Binding IsCreating}" />
</Border>

Two Context Menu Sharing Icons but only one context menu icons shows

I am having two Context Menus like Below, both are having same syntax and are resources of different controls.
<ContextMenu ItemsSource="{Binding Actions}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MetroMenuItem}">
<Setter Property="Header" Value="{Binding Title}"/>
<Setter Property="ToolTip" Value="{Binding ToolTips}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="Icon" Value="{Binding Icon}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
All works fine but only problem with Icon.
ViewModel
I have a property like below,
public Image Icon
{
get { return _Icon; }
set{ _Icon = value; NotifyPropertyChanged(); }
}
I am initializing it like below,
Icon = new Image
{
Source = new BitmapImage(new Uri(#"../images/ReIndex.png", UriKind.Relative)),
Height = 20,
Width = 20,
Margin = new Thickness(5)
};
The issue is that if one context menu shows the icon the other wont.
I know MenuItem.Icon is an Object. so i tried to using BitmapImage instead of Image directly but still i have the issue.
Edit/Solved
Added a resource like below,
<Control.Resources>
<Image x:Shared="False" x:Key="Icon" Source="{Binding Icon}" Height="20" Width="20"/>
</Control.Resources>
Then My Context Menu looks like below,
<ContextMenu ItemsSource="{Binding Actions}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MetroMenuItem}">
<Setter Property="Header" Value="{Binding Title}"/>
<Setter Property="ToolTip" Value="{Binding ToolTips}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="Icon" Value="{StaticResource Icon}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
ViewModel Now
public BitmapImage Icon
{
get { return _Icon; }
set{ _Icon = value; NotifyPropertyChanged(); }
}
Icon = new BitmapImage(new Uri(#"../images/Pencil-01.png", UriKind.Relative));
Main point is x:Shared="False" in the Image Control.
If any other good solution appreciated.
Added a resource like below,
<Control.Resources>
<Image x:Shared="False" x:Key="Icon" Source="{Binding Icon}" Height="20" Width="20"/>
</Control.Resources>
Then My Context Menu looks like below,
<ContextMenu ItemsSource="{Binding Actions}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MetroMenuItem}">
<Setter Property="Header" Value="{Binding Title}"/>
<Setter Property="ToolTip" Value="{Binding ToolTips}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="Icon" Value="{StaticResource Icon}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
ViewModel
public BitmapImage Icon
{
get { return _Icon; }
set{ _Icon = value; NotifyPropertyChanged(); }
}
Icon = new BitmapImage(new Uri(#"../images/Pencil-01.png", UriKind.Relative));
Main point is x:Shared="False" in the Image Control.

WPF Enable Disable Border based on MultiDataTrigger not working

I have a border in WPF that I am trying to enable or disable based on two properties in the viewmodel: ConnectedVisibility and OperatingMode. This data trigger disables the border when connectedvisibility visibility is not set to "Visible". But it does not work for OperatingMode. For OperatingMode other than 0, the border should be disabled but it stays enabled. It appears that there is no impact of OperatingMode changing its value at all. Even the breakpoints that I put in the convertor are not being hit except when the program first starts up. The bindings look Ok as there is no problem shown in Debug output for these bindings. Any help is appreciated.
The Style is
<Style x:Key="EnableOnConnectBorderCorrected" TargetType="{x:Type Border}">
<!--<Setter Property="BorderBrush" Value="#FFDADADA"/>-->
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="2"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ConnectedVisibility}" Value="Visible"/>
<Condition Binding="{Binding OperatingMode}" Value="0"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
<DataTrigger Binding="{Binding OperatingMode, Converter={x:Static VM:IsEqualOrGreaterThanSHORTConverter.Instance}, ConverterParameter=1,Mode=TwoWay}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding ConnectedVisibility}" Value="Collapsed">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
The convertor used in the style is ->
public class IsEqualOrGreaterThanSHORTConverter : IValueConverter
{
public static readonly IValueConverter Instance = new IsEqualOrGreaterThanSHORTConverter();
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
short iValue = (short)value;
short compareToValue = System.Convert.ToInt16(parameter);
return iValue >= compareToValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The XAML where this style is applied is
<Border Name="RebootDash" Grid.Row="2" Grid.Column="1" Style="{StaticResource EnableOnConnectBorderCorrected}" BorderBrush="#FFDADADA" BorderThickness="1" CornerRadius="2" Width="Auto" Margin="0,1,1,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="2" Background="Wheat"/>
<telerik:RadButton Command="{Binding ResetUnitCommand, Source={StaticResource UnitCommandProvider}}" Style="{StaticResource DashBoardImageButton}">
<Image Grid.Row="0" Source="/Images/UnitManagementImages/IMG_THOR_UNITResetUnit128.png"
ToolTip="{x:Static properties:Resources.Unit_Command_ResetUnit}"
Width="40" Height="40"
Margin="0,5,0,5"
HorizontalAlignment="Center"/>
</telerik:RadButton>
<TextBlock Grid.Row="2" Text="{x:Static properties:Resources.Unit_Command_ResetUnit}" HorizontalAlignment="Center" Margin="5,5,5,5"/>
</Grid>
</Border>
The properties to which it is bound are
public Visibility ConnectedVisibility
{
get { return connectedVisibility; }
set
{
if (connectedVisibility == value) return;
connectedVisibility = value;
RaisePropertyChanged("ConnectedVisibility");
}
}
public short OperatingMode
{
get { return UnitOperatingModeVM.OperatingMode; }
set
{
UnitOperatingModeVM.OperatingMode = value;
}
}
since you have only one condition to enable the same, so perhaps setting IsEnabled to False by default should do the trick
<Style x:Key="EnableOnConnectBorderCorrected" TargetType="{x:Type Border}">
<!--<Setter Property="BorderBrush" Value="#FFDADADA"/>-->
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="2"/>
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ConnectedVisibility}" Value="Visible"/>
<Condition Binding="{Binding OperatingMode}" Value="0"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
I have added <Setter Property="IsEnabled" Value="False"/> in the style which will by default disable the control and I have removed the other redundant conditions
so as result when the both conditions in MultiDataTrigger will meet it will enable the same, otherwise it remains disabled
above example assumes that both of the property in MultiDataTrigger ConnectedVisibility & OperatingMode are notifying the changes.
you may also need to add the notification for OperatingMode in order for MultiDataTrigger to fire the trigger
public short OperatingMode
{
get { return UnitOperatingModeVM.OperatingMode; }
set
{
UnitOperatingModeVM.OperatingMode = value;
RaisePropertyChanged("OperatingMode");
}
}
Is your OperatingMode property set implementation call RaisePropertyChanged("OperatingMode");
private short operatingMode;
public short OperatingMode
{
get
{
return operatingMode;
}
set
{
if (operatingMode != value)
{
operatingMode = value;
this.RaisePropertyChanged("OperatingMode");
}
}
}

Categories

Resources