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;
}
}
Related
I would like to slide the text (marquee text) of the selected item in a combobox, if it's lenght is bigger than the width of the combobox. It can be either automatical or when the user put the mouse over the combobox. The problem is that i have absolutely no idea on how to do that. It's maybe possible to do that with a render transform (previous definition of a textblock inside it)? or with a storyboard?
Here is the xaml that i need to modify
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate" >
<Setter.Value>
<DataTemplate DataType="DataGridColumnHeader" >
<ComboBox ItemContainerStyle="{StaticResource SingleSelectionComboBoxItem}" DisplayMemberPath="Oggetto" Width="100" Height="20" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},Path=DataContext.Selezione, UpdateSourceTrigger=LostFocus}" SelectionChanged="SingleSelectionComboBox_SelectionChanged"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
EDIT: the problem is that i don't know which properties should i target in the storyboard
EDIT2: i took the template of the combobox and modified the text box section like this :
<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0" To="100" Duration="00:00:10" Storyboard.TargetProperty="X" Storyboard.TargetName="transferCurreny" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform x:Name="transferCurreny" X="0"/>
</Setter.Value>
</Setter>
The problem is that this seems to have no effect
EDIT 3: i realized that i had to use the template that use the style i mentioned above
<ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="Placement" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2"
IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
MinWidth="{Binding ActualWidth, ElementName=Placement}">
<Border x:Name="DropDownBorder"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"
BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect"
Fill="{Binding Background, ElementName=DropDownBorder}"
Height="{Binding ActualHeight, ElementName=DropDownBorder}"
Width="{Binding ActualWidth, ElementName=DropDownBorder}" />
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter"
KeyboardNavigation.DirectionalNavigation="Contained"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<Themes:ListBoxChrome x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" Grid.ColumnSpan="2"
RenderMouseOver="{TemplateBinding IsMouseOver}"
RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" />
<TextBox x:Name="PART_EditableTextBox"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"
Margin="{TemplateBinding Padding}" Style="{StaticResource ComboBoxEditableTextBox}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" >
</TextBox>
<ToggleButton Grid.Column="1"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ComboBoxToggleButton}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="Foreground" Value="Black" />
</Trigger>
<Trigger Property="IsDropDownOpen" Value="true">
<Setter Property="RenderFocused" TargetName="Border" Value="true" />
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="DropDownBorder" Value="95" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
<Setter Property="Background" Value="#FFF4F4F4" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true" />
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</MultiTrigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5" />
<Setter Property="Color" TargetName="Shdw" Value="#71000000" />
</Trigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="OpaqueRect"
Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}" />
<Setter Property="Canvas.Left" TargetName="OpaqueRect"
Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
the textbox section is the one that use the style. However when i do Template="{StaticResource ComboBoxEditableTemplate}" in the combobox, i cannot see the selected item anymore, even if it's not null
Updated 08/02
Basically in order to avoid the animation issues you are facing - you will need to add a ScrollViewer as a wrapper to the content-site or selection-box TextBlock. You can either do that by using a custom template selector for your ComboBox so as defined below, or provide a custom item-template with ScollViewer.
Secondly, you can either create a custom ComboBox control in C# to bind the above template-selector/item-template and apply slide-animation on MouseEnterEvent; or implement the same in XAML itself.
For this example - have implemented the control in C#, and provided
a sample usage in XAML.
Code for custom control, and template selector
public class SlidingComboBox : ComboBox
{
public static readonly DependencyProperty SlideForeverProperty = DependencyProperty.Register("SlideForever", typeof(bool), typeof(SlidingComboBox), new FrameworkPropertyMetadata(false));
public bool SlideForever
{
get { return (bool)GetValue(SlideForeverProperty); }
set { SetValue(SlideForeverProperty, value); }
}
protected ContentPresenter _parent;
protected DoubleAnimation _animation;
protected TranslateTransform _translate;
protected Storyboard _storyBoard;
public SlidingComboBox()
{
Loaded += ExComboBox_Loaded;
ClipToBounds = true;
//assign template selector - just to re-template ContentSite / selection box
//uncomment this code - if you want to default-template for popup-items
//ItemTemplateSelector = new SlidingComboBoxItemTemplateSelector();
}
private void ExComboBox_Loaded(object sender, RoutedEventArgs e)
{
Loaded -= ExComboBox_Loaded;
//get content-site holder/parent
_parent = this.GetChildOfType<ContentPresenter>();
//setup slide animation
_animation = new DoubleAnimation()
{
From = 0,
RepeatBehavior = SlideForever ? RepeatBehavior.Forever : new RepeatBehavior(1), //repeat only if slide-forever is true
AutoReverse = SlideForever
};
//create storyboard
_storyBoard = new Storyboard();
_storyBoard.Children.Add(_animation);
Storyboard.SetTargetProperty(_animation, new PropertyPath("RenderTransform.(TranslateTransform.X)"));
}
protected override void OnMouseEnter(MouseEventArgs e)
{
//get actual textblock that renders the selected value
var textBlock = _parent.GetChildOfType<TextBlock>();
//and translate-transform for animation
textBlock.RenderTransform = _translate = new TranslateTransform();
//start animation only if text-block width is greater than parent
if (_parent.ActualWidth < textBlock.ActualWidth)
{
_animation.Duration = TimeSpan.FromMilliseconds(((int)textBlock.Text?.Length * 100));
_animation.To = _parent.ActualWidth - textBlock.ActualWidth;
_storyBoard.Begin(textBlock);
}
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
//stop animation once mouse pointer is off the control
_storyBoard.Stop();
//reset render state
var textBlock = _parent.GetChildOfType<TextBlock>();
textBlock.RenderTransform = _translate = new TranslateTransform();
base.OnMouseLeave(e);
}
}
public class SlidingComboBoxItemTemplateSelector : DataTemplateSelector
{
DataTemplate _selectedItemTemplate;
public SlidingComboBoxItemTemplateSelector()
{
//create datatemplate with ScrollViewer and TextBlock as child
var textBlock = new FrameworkElementFactory(typeof(TextBlock));
textBlock.SetValue(TextBlock.TextWrappingProperty, TextWrapping.NoWrap);
textBlock.SetBinding(TextBlock.TextProperty, new Binding("SelectedValue")
{
RelativeSource = new RelativeSource { Mode = RelativeSourceMode.FindAncestor, AncestorType = typeof(ComboBox) }
});
var scrollViewer = new FrameworkElementFactory(typeof(ScrollViewer));
scrollViewer.SetValue(ScrollViewer.CanContentScrollProperty, true);
scrollViewer.SetValue(ScrollViewer.HorizontalScrollBarVisibilityProperty, ScrollBarVisibility.Hidden);
scrollViewer.SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Disabled);
scrollViewer.AppendChild(textBlock);
_selectedItemTemplate = new DataTemplate
{
VisualTree = scrollViewer
};
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
ComboBoxItem comboBoxItem = container.GetVisualParent<ComboBoxItem>();
if (comboBoxItem == null)
{
//send back only if template requested for ContentSite, and not for combo-box item(s)
return _selectedItemTemplate;
}
return null;
}
}
/// <summary>
/// VisualTree helper
/// </summary>
public static class HelperExtensions
{
public static T GetChildOfType<T>(this DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
public static T GetVisualParent<T>(this DependencyObject child) where T : Visual
{
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
}
Sample usage:
<Window x:Class="MarqueeSample.MainWindow"
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:MarqueeSample"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="400">
<Grid Margin="20">
<DataGrid AutoGenerateColumns="False" IsReadOnly="True" Width="225" VerticalAlignment="Center">
<DataGrid.ItemsSource>
<col:Hashtable>
<col:ArrayList x:Key="TestData1">
<sys:String>Tiny</sys:String>
<sys:String>Keep calm and love programming</sys:String>
</col:ArrayList>
<col:ArrayList x:Key="TestData2">
<sys:String>Sample string</sys:String>
<sys:String>Another string to test</sys:String>
</col:ArrayList>
</col:Hashtable>
</DataGrid.ItemsSource>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="100" Binding="{Binding Key}" />
<DataGridTemplateColumn Header="Value" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:SlidingComboBox ItemsSource="{Binding Value}">
<local:SlidingComboBox.ItemTemplate>
<DataTemplate>
<ScrollViewer CanContentScroll="True" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Disabled">
<TextBlock Text="{Binding}" />
</ScrollViewer>
</DataTemplate>
</local:SlidingComboBox.ItemTemplate>
</local:SlidingComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
You can also use SlideForever property to manipulate RepeatBehaviour.
<local:SlidingComboBox ItemsSource="{Binding Value}" SlideForever="True" />
I've been looking into this and I think I have your solution. You should combine both a RenderTransform and a Storyboard on the ComboBox ContentPresenter (this is what displays the currently selected item)
<ComboBox Grid.Row="1" Name="MyComboBox" Width="200">
<ComboBox.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform X="0" Y="0" />
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<EventTrigger.Actions>
<BeginStoryboard x:Name="ScrollItem">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation Duration="00:00:5" From="0" To="200" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" />
<DoubleAnimation Duration="00:00:5" BeginTime="00:00:5" From="-200" To="0" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</ComboBox.Resources>
<ComboBoxItem>
I am combobox value 1
</ComboBoxItem>
<ComboBoxItem>
I am combobox value 2, Hello!
</ComboBoxItem>
</ComboBox>
Having the ComboBox of size 200, scrolling from 0 to 200, then -200 to 0, scrolls the text off the right hand side of the control, and in the left hand side. (You can drop the 2nd DoubleAnimation if you like and set AutoReverse to True to cause the text to bounce back in if you'd rather that. This does not code you around items that are too big for the control, you will need to write some code for the ComboBox so it decides if the currently selected Item is too big, and from code behind (or maybe a custom ComboBox Class) dynamically turn on/off the storyboard.
First iterate through all items of your combobox, check for the width of every items by assigning the text to a label. Then, check width every time, if width of current item gets greater than previous items then change the maximum width.
int DropDownWidth(ComboBox myCombo)
{
int maxWidth = 0;
int temp = 0;
Label label1 = new Label();
foreach (var obj in myCombo.Items)
{
label1.Text = obj.ToString();
temp = label1.PreferredWidth;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
label1.Dispose();
return maxWidth;
}
private void window_loaded(object sender, EventArgs e)
{
comboBox1.DropDownWidth = DropDownWidth(comboBox1);
}
OR
int DropDownWidth(ComboBox myCombo)
{
int maxWidth = 0, temp = 0;
foreach (var obj in myCombo.Items)
{
temp = TextRenderer.MeasureText(obj.ToString(), myCombo.Font).Width;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
return maxWidth;
}
Thanks to those guys got it from here --> Auto-width of ComboBox's content
I want to show my GroupName on left and button "ShowOnly" on the right of header row.
I tried with this code but doesn't works.
Could anyone help me?
Thx
My code :
<Expander IsExpanded="True" >
<Expander.Header>
<DockPanel HorizontalAlignment="Stretch">
<TextBlock Text="{Binding Path=Name}" FontSize="18"></TextBlock>
<Button Style="{StaticResource ButtonStyle}" Content="Show Only" HorizontalAlignment="Right" Padding="15,0,15,0" Click="Button_Click"></Button>
</DockPanel>
</Expander.Header>
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="Background" Value="#f0f0f5"></Setter>
<Setter Property="TextElement.FontFamily" Value="Arial Nova"/>
</Style>
</Expander.Style>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
My ButtonStyle :
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Background" Value="#66e0ff" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="15" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="4" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="White" />
<Setter Property="Foreground" Value="#66e0ff" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I tried also with an DockPanel, but same result.
You need to set the HorizontalAlignment property of the ContentPresenter that is defined in the Expander's default control template to Stretch.
The easiest way to do this is probably to handle the Loaded event of the Expander and use a helper method that finds the ContentPresenter in the visual tree:
private void Expander_Loaded(object sender, RoutedEventArgs e)
{
Expander expander = sender as Expander;
System.Windows.Controls.Primitives.ToggleButton HeaderSite = GetChildOfType<System.Windows.Controls.Primitives.ToggleButton>(expander);
if (HeaderSite != null)
{
ContentPresenter cp = GetChildOfType<ContentPresenter>(HeaderSite);
if (cp != null)
cp.HorizontalAlignment = HorizontalAlignment.Stretch;
}
}
private static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
<Expander IsExpanded="True" Loaded="Expander_Loaded">
<Expander.Header>
<DockPanel HorizontalAlignment="Stretch">
<Button Style="{StaticResource ButtonStyle}" Content="Show Only" DockPanel.Dock="Right" Padding="15,0,15,0" Click="Button_Click"></Button>
<TextBlock Text="{Binding Path=Name}" FontSize="18"></TextBlock>
</DockPanel>
</Expander.Header>
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="Background" Value="#f0f0f5"></Setter>
<Setter Property="TextElement.FontFamily" Value="Arial Nova"/>
</Style>
</Expander.Style>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
Set width of DockPanel to width of expander so that it will stretch properly.
<DockPanel HorizontalAlignment="Stretch" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Expander}}, Path=ActualWidth}">
You can refer to these solutions:
https://joshsmithonwpf.wordpress.com/2007/02/24/stretching-content-in-an-expander-header/
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>
I had implemented a template for textbox to show image inside it, but the problem I facing is that I unable to show image dynamically. Here is my code
Window.xaml
<TextBox Style="{StaticResource imageTextBox}" />
Here is my textbox template
<Style TargetType="Control" x:Key="imageTextBox">
<Setter Property="Margin" Value="0,0,0,8"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<Border x:Name="bg" BorderThickness="1" CornerRadius="3" BorderBrush="Gray">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Style="{DynamicResource BasicTextBox}" Grid.Column="1"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}"
MinWidth="340" Margin="1" />
<Image Source="Images/img_1.gif" Width="20"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="bg" Value="Black"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" TargetName="bg" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BasicTextBox" TargetType="{x:Type TextBox}">
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
You can use a {Binding} for the <Image.Source> property. Here's an example as a LinqPad query (get LinqPad free):
void Main()
{
// Need to parse XAML since LinqPad doesn't have an integrated XAML build
var xaml = #"
<Grid xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<Grid.Resources>
<Style TargetType=""Control"" x:Key=""imageTextBox"">
<Setter Property=""Margin"" Value=""0,0,0,8""/>
<Setter Property=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""Control"">
<Border x:Name=""bg"" BorderThickness=""1"" CornerRadius=""3"" BorderBrush=""Gray"">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=""30""/>
<ColumnDefinition Width=""*""/>
</Grid.ColumnDefinitions>
<TextBox Style=""{DynamicResource BasicTextBox}"" Grid.Column=""1""
Foreground=""{TemplateBinding Foreground}""
Background=""{TemplateBinding Background}""
FontFamily=""{TemplateBinding FontFamily}""
FontSize=""{TemplateBinding FontSize}""
FontWeight=""{TemplateBinding FontWeight}""
MinWidth=""340"" Margin=""1"" />
<Image Source=""{Binding Image}"" Width=""20""/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property=""IsMouseOver"" Value=""True"">
<Setter Property=""BorderBrush"" TargetName=""bg"" Value=""Black""/>
</Trigger>
<Trigger Property=""IsFocused"" Value=""True"">
<Setter Property=""BorderBrush"" TargetName=""bg"" Value=""Black""/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<TextBox Style=""{StaticResource imageTextBox}"" />
</Grid>
";
// Get some images... these could be in your resources
var png = PngBitmapDecoder.Create(new Uri("https://upload.wikimedia.org/wikipedia/commons/9/97/Esperanto_star.png"), BitmapCreateOptions.None, BitmapCacheOption.Default);
var vm = new ViewModel { Image = png.Frames[0] };
var o = (FrameworkElement)XamlReader.Parse(xaml);
// Set the image source - in this case, a view model
o.DataContext = vm;
// Let LinqPad display it
PanelManager.DisplayWpfElement(o);
// This code is for the example only, to change the image after 2 seconds.
var dispatcher = o.Dispatcher;
Task.Run(async () =>
{
await Task.Delay(2000);
await dispatcher.BeginInvoke((Action)(() =>
{
png = PngBitmapDecoder.Create(new Uri("https://upload.wikimedia.org/wikipedia/commons/f/f6/Lol_question_mark.png"), BitmapCreateOptions.None, BitmapCacheOption.Default);
vm.Image = png.Frames[0];
}));
});
}
// Define other methods and classes here
public class ViewModel : INotifyPropertyChanged
{
private ImageSource _image;
public event PropertyChangedEventHandler PropertyChanged;
public ImageSource Image
{
get
{
return _image;
}
set
{
if (_image == value)
{
return;
}
_image = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Image)));
}
}
}
This example uses a simple view model as the Binding source, but it could be any source. There is also code which changes the Image value after a 2 second delay to show you that it does in fact update. In your code, you'd omit this and use the way you want to update it.
At first it appears:
Then after update:
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;
}
}
}