Change ContentPresenter's DataTemplate - c#

I have a popup, where I want to display different things depending on various buttons which are clicked. To do this I've added a ContentPresenter nad in this ContentPresenter I've got an TemplateSelector. My problem is that as far as I can see it only checks which template to use the first time my popUp is run and uses this template from then on. Is there a way to get the code to change the template to use?
The code I've got so far is (xaml):
<Popup IsOpen="{Binding IsOpen}" Height="{Binding Height}" Width="{Binding Width}">
<Grid>
<ContentPresenter x:Name="CP" Loaded="CP_Loaded">
<ViewModel:PopUpTemplateSelector x:Name="PUT" Content="{Binding}">
<ViewModel:PopUpTemplateSelector.View1>
<DataTemplate>
<View:View1/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View1>
<ViewModel:PopUpTemplateSelector.View2>
<DataTemplate>
<View:View2/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View2>
<ViewModel:PopUpTemplateSelector.View3>
<DataTemplate>
<View:View3/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View3>
<ViewModel:PopUpTemplateSelector.View4>
<DataTemplate>
<View:View4/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View4>
<ViewModel:PopUpTemplateSelector.View5>
<DataTemplate>
<Design:View5/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View5>
</ViewModel:PopUpTemplateSelector>
</ContentPresenter>
</Grid>
</Popup>
and my popUpTemplateSelector(C#) is
public class PopUpTemplateSelector : DataTemplateSelector
{
public DataTemplate View1{ get; set; }
public DataTemplate View2 { get; set; }
public DataTemplate View3 { get; set; }
public DataTemplate View4 { get; set; }
public DataTemplate View5 { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
PopUpViewModel Pop = item as PopUpViewModel;
if(Pop.TemplateToUse == "View1")
{
return View1;
}
else if(Pop.TemplateToUse == "View2")
{
return View2;
}
else if(Pop.TemplateToUse.Equals("View3"))
{
return View3;
}
else if (Pop.TemplateToUse.Equals("View4"))
{
return View4;
}
else if(Pop.TemplateToUse.Equals("View5"))
{
return View5;
}
return null;
}
}

I would suggest you use DataTriggers bound to TemplateToUse property on the ViewModel to update ContentTemplate. And also use ContentControl instead of ContentPresenter
<Popup IsOpen="{Binding IsOpen}" Height="{Binding Height}" Width="{Binding Width}">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="View1Template">
<View:View1/>
</DataTemplate>
<DataTemplate x:Key="View2Template">
<View:View2/>
</DataTemplate>
<DataTemplate x:Key="View3Template">
<View:View3/>
</DataTemplate>
<DataTemplate x:Key="View4Template">
<View:View4/>
</DataTemplate>
<DataTemplate x:Key="View5Template">
<Design:View5/>
</DataTemplate>
<Style x:Key="ContentStyle" TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View1">
<Setter Property="ContentTemplate" Value="{StaticResource View1Template}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View2">
<Setter Property="ContentTemplate" Value="{StaticResource View2Template}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View3">
<Setter Property="ContentTemplate" Value="{StaticResource View3Template}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View4">
<Setter Property="ContentTemplate" Value="{StaticResource View4Template}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View5">
<Setter Property="ContentTemplate" Value="{StaticResource View5Template}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ContentControl x:Name="CP" Loaded="CP_Loaded" Style="{StaticResource ContentStyle}" Content="{Binding}" />
</Grid>
</Popup>

Related

XAML: Show button on ComboBox item highlight

I have a ComboBox with an ItemTemplateSelector, using 2 different DataTemplates, one for when its drop down is visible and another when it is not. For the drop down template, each ComboBox item is represented by a TextBlock and a Button that should only be visible whenever that item is focused/highlighted/mouse over. This is what I've tried:
<ComboBox x:Name="Windows" ItemsSource="{Binding Windows}" SelectedItem="{Binding Window}" Focusable="False" MaxDropDownHeight="238">
<ComboBox.ItemTemplateSelector>
<s:ComboBoxItemTemplateSelector>
<s:ComboBoxItemTemplateSelector.SelectedTemplate>
<DataTemplate>
<TextBlock Text="{Binding TitleShort}" ToolTip="{Binding Title}" />
</DataTemplate>
</s:ComboBoxItemTemplateSelector.SelectedTemplate>
<s:ComboBoxItemTemplateSelector.DropDownTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{Binding TitleShort}" />
<Button Content="X">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=Windows}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</DataTemplate>
</s:ComboBoxItemTemplateSelector.DropDownTemplate>
</s:ComboBoxItemTemplateSelector>
</ComboBox.ItemTemplateSelector>
<ComboBox.ItemContainerStyle>
<Style BasedOn="{StaticResource MaterialDesignComboBoxItemStyle}" TargetType="ComboBoxItem">
<Setter Property="ToolTip">
<Setter.Value>
<TextBlock Text="{Binding Title}" />
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
public DataTemplate SelectedTemplate { get; set; }
public DataTemplate DropDownTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
ComboBoxItem comboBoxItem = GetVisualParent<ComboBoxItem>(container);
if (comboBoxItem == null)
{
return SelectedTemplate;
}
return DropDownTemplate;
}
private static T GetVisualParent<T>(object childObject) where T : Visual
{
DependencyObject child = childObject as DependencyObject;
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
}
ComboBox generates ComboBoxItem as a container for every item in its itemssource. You can bind to its properties with RelativeSource binding.
This should get you the expected behavior:
<Button Content="X">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=ComboBoxItem}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Change button tooltip with style - WPF

I have a DataGrid loaded with a list of objects that have a property bool IsAutomaticSell. I need that when changing the value, the tooltip of the button of that row is updated. I have the following code but it does not work. Thx
View.xaml
<DataGridTextColumn Header="Code"
Binding="{Binding Code}" />
<DataGridTemplateColumn Header="Actions"
Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Command="{Binding DataContext.AutomaticSellCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}"
Padding="10"
Margin="0,2,2,2">
<iconPacks:PackIconModern Kind="CurrencyDollar" />
<Button.Style>
<Style TargetType="{x:Type Button}"
BasedOn="{StaticResource AccentedSquareButtonStyle}">
<Setter Property="ToolTip"
Value="DEFAULT_TOOLTIP" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsAutomaticSell, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="ToolTip"
Value="NEW_TOOLTIP" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
ViewModel.cs
public ICommand AutomaticSellCommand => _automaticSellCommand ??
(_automaticSellCommand = new RelayCommand<OrderStatusDataWrapper>(AutomaticSell));
private static void AutomaticSell(OrderStatusDataWrapper orderStatusData)
{
orderStatusData.IsAutomaticSell = !orderStatusData.IsAutomaticSell;
}
Bind ToolTip.
XAML:
<Button ToolTip={Binding ToolTip}" ... />
ViewModel:
public string ToolTip => (IsAutomaticSell) ? "DEFAULT_TOOLTIP" : "NEW_TOOLTIP";
You need to implement a ValueConverter in order to get this to work.
A ValueConverter will allow you to handle what to display based on your boolean value.
MSDN
public class BoolToContentConverter : IValueConverter
{
public BoolToContentConverter()
{
TrueContent = "True Tool Tip";
FalseContent = "False Tool Tip";
NullContent = "No Value";
}
public object TrueContent { get; set; }
public object FalseContent { get; set; }
public object NullContent { get; set; }
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value == null)
return NullContent;
bool boolValue = true;
bool isBool = true;
try
{
boolValue = (bool) value;
}
catch
{
isBool = false;
}
if (!isBool)
return NullContent;
return boolValue ? TrueContent : FalseContent;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
In your XAML you need to implement that converter.
<Window x:Class="Example.MainWindow"
...
xmlns:l="clr-Example"
...>
<Window.Resources>
<l:BoolToContentConverter x:Key="converter" />
</Window.Resources>
...
<DataGridTextColumn Header="Code"
Binding="{Binding Code}" />
<DataGridTemplateColumn Header="Actions"
Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Command="{Binding DataContext.AutomaticSellCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}"
Padding="10"
Margin="0,2,2,2">
<iconPacks:PackIconModern Kind="CurrencyDollar" />
<Button.Style>
<Style TargetType="{x:Type Button}"
BasedOn="{StaticResource AccentedSquareButtonStyle}">
<Setter Property="ToolTip"
Value="DEFAULT_TOOLTIP" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsAutomaticSell, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="ToolTip"
Value="{Binding IsAutomaticSell, Converter={StaticResource converter}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
....
</Window>
Hope this answers your question. Cheers!

Create Hierarchical ContextMenu dynamically MVVM

I would like to create dynamically Hierarchical ContextMenu from data in ViewModel.
In ViewMode, I defined ContextMenuAction:
public class ContextMenuAction : ViewModelBase
{
public string Header { get; set; }
public ICommand Action { get; set; }
public Brush Icon { get; set; }
public ObservableCollection<ContextMenuAction> SubActions { get; set; } = new ObservableCollection<ContextMenuAction>();
}
In View:
<ContextMenu ItemsSource="{Binding Path=PlacementTarget.Tag.Actions, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemTemplate >
<DataTemplate DataType="MenuItem">
<MenuItem/>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="ItemsSource" Value="{Binding SubActions}"/>
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Command" Value="{Binding Action}"/>
</Style>
</ContextMenu.ItemContainerStyle>
This is result, there no text in context menu.
I already check output window to check binding, all bindings work, there is no exception.
Please help me to find out the reason, thank in advance!
You should define a HierarchicalDataTemplate:
<ContextMenu ItemsSource="{Binding Path=PlacementTarget.Tag.Actions, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ContextMenuAction}" ItemsSource="{Binding SubActions}">
<TextBlock Text="{Binding Header}" />
</HierarchicalDataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Action}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>

DataTrigger binded to property in ViewModel is not fired in the Button of DataGrid

I've created ControlTemplates:
<Window.Resources>
<ControlTemplate x:Key="imgNo" TargetType="{x:Type Control}">
<Image Source="pack://application:,,,/Images/up.png"/>
</ControlTemplate>
<ControlTemplate x:Key="imgUp" TargetType="{x:Type Control}">
<!--<TextBlock Text="Up"/>-->
<Image Source="pack://application:,,,/Images/up.png"/>
</ControlTemplate>
<ControlTemplate x:Key="imgDown" TargetType="{x:Type Control}">
<Image Source="pack://application:,,,/Images/downArrow.png"/>
</ControlTemplate>
<DataTemplate x:Key="ButtonOneDataTemplate">
<Control x:Name="theControl" Template="{DynamicResource imgNo}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsImageChanged}" Value="true">
<Setter TargetName="theControl" Property="Template" Value="{DynamicResource imgUp}" />
</DataTrigger>
<DataTrigger Binding="{Binding IsImageChanged}" Value="false">
<Setter TargetName="theControl" Property="Template" Value="{DynamicResource imgDown}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</WindowResources>
and Button in DataGrid which uses above ControlTemplates:
<DataGrid ItemsSource="{Binding Persons}" Grid.Row="1" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding IdPerson}">
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<Border Background="Violet">
<StackPanel>
<Button ContentTemplate="{StaticResource ButtonOneDataTemplate}"
Command="{Binding DataContext.HelloCommand, RelativeSource=
{RelativeSource AncestorType=Window}}"
CommandParameter="{Binding DataContext.Hello,
RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</StackPanel>
</Border>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
My ViewModel:
public class MainWindowViewModel:ViewModelBase
{
public RelayCommand HelloCommand { get; set; }
public MainWindowViewModel()
{
LoadPersons();
HelloCommand = new RelayCommand(SayHello);
}
int helloCounter = 0;
private void SayHello(object obj)
{
if (helloCounter % 2 == 0)
IsImageChanged = true;
else
IsImageChanged = false;
helloCounter++;
}
private bool isImageChanged=true;
public bool IsImageChanged
{
get { return isImageChanged; }
set { isImageChanged = value;
OnPropertyChanged("IsImageChanged");
}
}
}
What I want is when I click on the button <Button ContentTemplate="{StaticResource ButtonOneDataTemplate}"/>, then Template should be replaced to {DynamicResource imgDown} or {DynamicResource imgUp}. DataTrigger depends on IsImageChanged value.
However, if I click on the Button, then DataTrigger is not fired(Controltemplates such as imgUp, imgDown are not changed). How can I achieve this from my ViewModel?
Problem is that DataGrid column is not a part of a visual tree, and because of that it does not inherit DataContext. To be able to use DataTriggers in your ButtonOneDataTemplate you need that button, you applying this template to, has correct DataContext. There is a trick, how to provide DataContext to elements that are not in VisualTree, described here
Applying that solution to your code we'll have the following:
Proxy
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Window.Resources
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
<DataTemplate x:Key="ButtonOneDataTemplate">
<Control x:Name="theControl" Template="{DynamicResource imgNo}" Foreground="Orange"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.IsImageChanged}" Value="True">
<Setter TargetName="theControl" Property="Template" Value="{DynamicResource imgUp}" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.IsImageChanged}" Value="False">
<Setter TargetName="theControl" Property="Template" Value="{DynamicResource imgDown}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
HeaderTemplate
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<Border Background="Violet">
<StackPanel>
<Button ContentTemplate="{StaticResource ButtonOneDataTemplate}"
DataContext="{Binding Path=Data, Source={StaticResource proxy}}"
Command="{Binding DataContext.ButtonClick, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</StackPanel>
</Border>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>

Binding on background color with a custom style on a button and checkbox

I am currently working on a WPF application and have redefined the buttons and checkbox to use a custom style in App.xaml.
App.xaml
<Style x:Key="{x:Type CheckBox}" TargetType="CheckBox">
<Setter Property="Margin" Value="0,0,10,5" />
<Setter Property="Width" Value="85" />
<Setter Property="Height" Value="50" />
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="FocusVisualStyle"
Value="{DynamicResource CheckBoxFocusVisual}" />
<Setter Property="Template" />
<Setter.Value />
<!-- ... etc. -->
</Style>
The code is from here with some modifications.
Now I want to change the background color through coding. For example, if the title of the checkbox is "a" then the background color would be red and so on. I succeeded in binding the titles but not the background color. My checkboxes are used inside a control item.
MainWindow.xaml
<Grid>
<ItemsControl Name="ControllerDisplay">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding controltitle}"
Style="{StaticResource MainControltitle}"/>
<CheckBox>
// want to change checkbox background
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}"
BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Background" Value="Aqua" />
</Style>
</CheckBox.Style>
<StackPanel >
<TextBlock Text="{Binding controlno}" HorizontalAlignment="Center"
FontSize="16" />
<TextBlock Text="{Binding status}" HorizontalAlignment="Center"
FontSize="10"/>
</StackPanel>
</CheckBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
on the coding side:
public void printcheckbox()
{
List<Controller> listItem = new List<Controller>();
listItem.Add(new Controller() { controltitle = "title1", controlno = "12345",
status = "On" , controlbg = Colors.Red});
ControllerDisplay.ItemsSource = listItem;
}
public class Controller
{
public string controltitle { get; set; }
public string controlno { get; set; }
public string status { get; set; }
public Color controlbg { get; set; }
}

Categories

Resources