XAML UserControl - Set Background based on Trigger - xaml

I have a UserControl that contains an Expander :
<UserControl x:Class="Client.DevicesExpander"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="devicesExpander" >
<Expander Margin="1" Name="expander" FontSize="11" BorderThickness="1" BorderBrush="DarkRed" Foreground="Black" Header="{Binding ElementName=devicesExpander, Path=Header}" FontWeight="Bold" MouseDoubleClick="Expander_MouseDoubleClick" Expanded="Expander_Expanded" IsExpanded="{Binding ElementName=devicesExpander, Path=IsExpanded}" Background="{Binding ElementName=devicesExpander, Path=Background}">
<StackPanel Name="_devicesPanel">
<ListBox BorderThickness="0,1,0,0" Name="_devicesList" FontWeight="Normal" MouseDoubleClick="DevicesList_MouseDoubleClick" Background="{Binding ElementName=devicesExpander, Path=Background}" />
</StackPanel>
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="True">
<Setter Property="Background" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
</Expander>
</UserControl>
Ans basically all I'd like to do is change the Expander and StackPanel's Background color based on the IsExpanded of the UserControl (or Expander).
I've added three Dependancy Properties to the control :
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(string), typeof(DevicesExpander));
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(DevicesExpander));
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(System.Windows.Media.Brush), typeof(DevicesExpander));
But my code does not work. The IsExpanded property from the usercontrol does work as the property changes accordingly (when the expander expands) when checked from within the window where the usercontrol is placed.
How can I change the background color of the Expander based on the UserControl.IsExpanded property ?
Thanks !
EDIT:
I have in the meanwhile done the following:
<UserControl.Resources>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="True">
<Setter Property="Background" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Expander Margin="1" Name="expander" FontSize="11" BorderThickness="1" BorderBrush="DarkRed" Foreground="Black" Header="{Binding ElementName=devicesExpander, Path=Header}" FontWeight="Bold" MouseDoubleClick="Expander_MouseDoubleClick" Expanded="Expander_Expanded" IsExpanded="{Binding ElementName=devicesExpander, Path=IsExpanded}" Background="Transparent">
<StackPanel Name="_devicesPanel">
<ListBox BorderThickness="0,1,0,0" Name="_devicesList" FontWeight="Normal" MouseDoubleClick="DevicesList_MouseDoubleClick" Background="Transparent" />
</StackPanel>
</Expander>
and removed the BackgroundProperty dependancy property. I actually thought this could work, but alas ...

I have managed to solve my problem. Herewith the solution (showing only the essential code) ...
My Dependancy Property is created as follows:
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(DevicesExpander));
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
and my XAML is :
<UserControl x:Class="TestApp.DevicesExpander"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="devicesExpander">
<Expander>
<Expander.Style>
<Style TargetType="Expander">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="True">
<Setter Property="Background" Value="Black" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="False">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
</Expander>
</UserControl>
The solution in the end was to remove the Backgroud property from the element and specify a DataTrigger for IsExpanded=True and IsExpanded=False. It thus seems like the Background property when specified in the element properties overrides anything that the triggers tried to set.
Hope this helps someone !

Herewith another solution to the same problem, this time using a IValueConverter ...
My XAML:
<UserControl.Resources>
<local:BackgroundConverter x:Key="backgroundConvertor" />
</UserControl.Resources>
<Expander>
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="Background" Value="{Binding ElementName=devicesEntry, Path=IsExpanded, Converter={StaticResource backgroundConvertor}}" />
</Style>
</Expander.Style>
</Expander>
and my code for the value convertor :
public class BackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? "White" : "Transparent";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string color = value as string;
if (color == "White")
return true;
return false;
}
}
I think though that I prefer the XAML only solution, although the IValueConverter option does make for slightly better reading of the XAML ...
(The DependancyProperty is still created exactly the same way)

Related

Xamarin Forms Tabbed Page Trigger properties not working

I'm trying to follow this sample
by using font awesome as icon for the tabbed page and use the triggers to change font color
but when I want to apply the style using font awesome I got an error that Property="IsChecked" does not exist for the target.
I noticed in the sample they are using shells how could I replicate that example using regular tabbed pages?
on the app.XAML I have the following
<OnPlatform x:TypeArguments="x:String"
x:Key="FontAwesomeSolid">
<On Platform="Android"
Value="Font5Solid.otf#Regular" />
<On Platform="iOS"
Value="FontAwesome5Free-Solid" />
</OnPlatform>
<OnPlatform x:TypeArguments="x:String"
x:Key="FontAwesomeRegular">
<On Platform="Android"
Value="Font5Regular.otf#Regular" />
<On Platform="iOS"
Value="FontAwesome5Free-Regular" />
</OnPlatform>
For the tabbed page I have the following
<?xml version="1.0" encoding="utf-8"?>
<TabbedPage x:Name="Tab" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="pages.Views.HomeTabbedPage">
<!--Pages can be added as references or inline-->
<TabbedPage.Resources>
<ResourceDictionary>
<Style TargetType="Tab" x:Key="FollowTab">
<Style.Triggers>
<Trigger TargetType="TabbedPage"
Property="IsChecked" Value="False">
<Setter Property="Icon" >
<Setter.Value>
<FontImageSource FontFamily="{StaticResource FontAwesomeRegular}" Glyph=""/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger TargetType="Tab"
Property="IsChecked" Value="True">
<Setter Property="Icon" >
<Setter.Value>
<FontImageSource FontFamily="{StaticResource FontAwesomeSolid}" Glyph=""/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</TabbedPage.Resources>
<ContentPage Title="sample page" />
</TabbedPage>
To replicate that in a normal TabbedPage you can try these steps:
1 - Create a converter to check TabbedPage.CurrentPage type:
public class SelectedTabTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return null;
if (!(value is Page))
throw new ArgumentException("Expected value to be of type " + nameof(Page), nameof(value));
// if value is a NavigationPage check against its RootPage
if (value is NavigationPage navPage)
return navPage.RootPage?.GetType();
return value.GetType();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
2 - Add the x:Name attribute to the TabbedPage so we can reference it latter:
<TabbedPage x:Name="MyTabbedPage" ...
3 - Add the converter resource to TabbedPage.Resources:
<TabbedPage.Resources>
<ResourceDictionary>
<converters:SelectedTabTypeConverter x:Key="SelectedTabTypeConverter" />
</ResourceDictionary>
</TabbedPage.Resources>
4 - Add the pages and the DataTriggers:
<TabbedPage.Children>
<!-- Normal page tab default values -->
<views:OnePage Title="Page1">
<views:OnePage.Triggers>
<DataTrigger
TargetType="views:OnePage"
Binding="{Binding Source={x:Reference MyTabbedPage}, Path=CurrentPage, Converter={StaticResource SelectedTabTypeConverter}}"
Value="{x:Type views:OnePage}">
<Setter Property="Title" Value="Page1 Selected" />
</DataTrigger>
</views:OnePage.Triggers>
</views:OnePage>
<!-- NavigationPage tab -->
<NavigationPage Title="Page2">
<x:Arguments>
<views:TwoPage />
</x:Arguments>
<NavigationPage.Triggers>
<DataTrigger
TargetType="NavigationPage"
Binding="{Binding Source={x:Reference MyTabbedPage}, Path=CurrentPage, Converter={StaticResource SelectedTabTypeConverter}}"
Value="{x:Type views:TwoPage}">
<Setter Property="Title" Value="Page2 Selected" />
</DataTrigger>
</NavigationPage.Triggers>
</NavigationPage>
</TabbedPage.Children>

WPF - MVVM - Combobox UserControl + ComboboxItem CustomControl

I am struggling to create a binding from a View + ViewModel to a Custom ComboboxItem. I am not sure it should work, I already had implemented complex UserControls and had to deal with setting the DataContext properly to make it work with MVVM, but this specific scenario doesn't work at all.
What I am trying to do:
Create a user control based on a Combobox. It will have Combobox behavior but it is customized. The combobox collapsed will show only a button without the Path, and when expanded (showing the dropdown list of combobox items), it will show more buttons customized.
This UserControl must work with MVVM (which is not working right now). If i set the ComboboxItem's content on the View hard coded they show correctly, but if I try to do a binding with viewmodel, it fails:
System.Windows.Data Error: 40 : BindingExpression path error: 'MessageName' property not found on 'object' ''MultiButtonControl' (Name='')'. BindingExpression:Path=MessageName; DataItem='MultiButtonControl' (Name=''); target element is 'TestComboBoxItem' (Name=''); target property is 'Content' (type 'Object')
I created :
A usercontrol MultiButtonControl containing a grid and a combobox with a specific style and a List called Children in the code behind (dependency property). I create a binding from the combobox on xaml with the Children property.
A custom control TestComboBoxItem.cs which extends from ComboBoxItem. And I created on Generic.xaml a style for that type.
DataContext:
I set the DataContext of the UserControl "MultiButtonControl.xaml" on the parent element -> Grid element as:
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
Currently working:
Right now the layout works fine. I can use my UserControl in the View, add the Children too. But the problem is the binding. I believe the problem is the DataContext. Because when I try to bind a property from ComboboxItem (TestComboBoxItem custom control) it fails, and if I set it hardcoded it works.
Let me give you the code:
View.xaml
<Window x:Class="Test2Manager.message.ModalMessageInsert"
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:Test2Manager.message"
xmlns:controls="clr-namespace:CSIncludes.controls;assembly=CSIncludes"
xmlns:wpf="clr-namespace:CSIncludes.wpf;assembly=CSIncludes"
mc:Ignorable="d"
WindowStyle="None"
AllowsTransparency="True"
WindowStartupLocation="CenterOwner"
WindowState="Maximized"
Background="#33000000"
Title="ModelMessageInsert"
Name="ModalWindow">
<Window.Resources>
<ResourceDictionary Source="/CSIncludes;component/Themes/Generic.xaml"/>
</Window.Resources>
<Grid Width="600" Height="400" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Gray">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"></TextBlock>
<Grid Grid.Row="1" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center">Name:</TextBlock>
<TextBox Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" Text="{Binding MessageName, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Effect>
<DropShadowEffect Color="Yellow" Direction="270" ShadowDepth="2" Opacity="1" BlurRadius="2"></DropShadowEffect>
</TextBox.Effect>
</TextBox>
<!--<controls:AudioControl Grid.Row="2" Grid.Column="2" RecordingMode="False" ReproduceAudioPath="C:\Fabio\Musicas\05 - On The Turning Away.mp3"></controls:AudioControl>-->
<controls:MultiButtonControl Grid.Row="2" Grid.Column="2" Width="170" ParentButtonText="{Binding MessageName}" ParentButtonImage="/CSIncludes;component/images/audio_play.png">
<controls:MultiButtonControl.Children>
<!--Command2="{Binding TestCommand}"-->
<wpf:TestComboBoxItem Content="{Binding MessageName}"></wpf:TestComboBoxItem>
<wpf:TestComboBoxItem Content="Fabio 2"></wpf:TestComboBoxItem>
<wpf:TestComboBoxItem>Fabio 3</wpf:TestComboBoxItem>
</controls:MultiButtonControl.Children>
</controls:MultiButtonControl>
<!--<ComboBox Style="{StaticResource CustomCombobox}">
<ComboBoxItem>Item1</ComboBoxItem>
<ComboBoxItem>Item2</ComboBoxItem>
<ComboBoxItem>Item3</ComboBoxItem>
<ComboBoxItem>Item4</ComboBoxItem>
<ComboBoxItem>Item5</ComboBoxItem>
</ComboBox>-->
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Center" Margin="5">
<Button Width="65" Command="{Binding SaveCommand}" CommandParameter="{Binding ElementName=ModalWindow}">Save</Button>
<Button Width="65" Command="{Binding CancelCommand}" CommandParameter="{Binding ElementName=ModalWindow}" Margin="5,0,0,0">Cancel</Button>
</StackPanel>
</Grid>
View - code-behind setting DataContext to ViewModel
using Arbeit.wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Test2Manager.message
{
/// <summary>
/// Interaction logic for ModelMessageInsert.xaml
/// </summary>
public partial class ModalMessageInsert : Window
{
ViewModelModalMessageInsert vm;
public ModalMessageInsert(Test2Entities Context, Action UpdateList)
{
InitializeComponent();
vm = new ViewModelModalMessageInsert(Context, UpdateList);
DataContext = vm;
}
}
}
ViewModel
using CSIncludes.wpf;
using Test2EF;
using Test2Manager.database;
using Test2Manager.manager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Test2Manager.message
{
class ViewModelModalMessageInsert
{
private Test2Entities Context;
private MessageDAL MessageDAL;
private Action UpdateList;
private ModelMessage Model;
public ViewModelModalMessageInsert(Test2Entities Context, Action UpdateList)
{
this.Context = Context;
this.UpdateList = UpdateList;
MessageDAL = new MessageDAL(Context);
Model = new ModelMessage();
Model.ClientId = LoggedManager.ClientId;
MessageName = "Teste";
}
public string MessageName
{
get { return Model.Name; }
set
{
Model.Name = value;
}
}
}
}
MultiButtonControl.xaml
<UserControl x:Class="CSIncludes.controls.MultiButtonControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CSIncludes.controls"
xmlns:wpf="clr-namespace:CSIncludes.wpf"
mc:Ignorable="d"
d:DesignHeight="55" d:DesignWidth="300">
<UserControl.Resources>
<!--<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
CornerRadius="0"
Background="#FF3F3F3F"
BorderBrush="#FF97A0A5"
BorderThickness="1" />
</Grid>
</ControlTemplate>-->
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}">
<Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
</ControlTemplate>
<Style x:Key="CustomCombobox" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="30"/>
<Setter Property="Foreground" Value="White"/>
<!--<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"></Setter>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<!--Template="{StaticResource ComboBoxToggleButton}"-->
<ToggleButton
VerticalAlignment="Center"
Height="{Binding ParentButtonHeight}"
Name="ToggleButton"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#5F1E78"></Setter>
<Setter Property="BorderBrush" Value="#5F1E78"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border BorderBrush="#FF97A0A5" BorderThickness="1" HorizontalAlignment="Center" Width="{TemplateBinding ActualWidth}">
<Grid Background="{TemplateBinding Background}" HorizontalAlignment="Center" Width="{TemplateBinding ActualWidth}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Margin="0,0,8,0" Foreground="White" Name="Text" Text="{Binding ParentButtonText}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Image Grid.Column="2" Name="Image" Width="16" Height="16" Source="{Binding ParentButtonImage}" />
</Grid>
<!--<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="16" />
</Grid.ColumnDefinitions>
<TextBlock Margin="10" Foreground="White" Grid.Column="0" Name="Text" Text="{Binding ParentButtonText}" HorizontalAlignment="Center" />
<Image Grid.Column="1" Name="Image" Source="{Binding ParentButtonImage}" />
</Grid>-->
</Border>
<!--<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="Text" Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<!--<ToggleButton Content="aaa"
Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>-->
<TextBox x:Name="PART_EditableTextBox" Style="{x:Null}" Height="{Binding ParentButtonHeight}" Template="{StaticResource ComboBoxTextBox}" HorizontalAlignment="Left"
VerticalAlignment="Center" Focusable="True" Background="White" Foreground="Black" Visibility="Hidden" IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup Name="Popup" Placement="Top" PlacementTarget="{Binding ElementName=PART_EditableTextBox}" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
<Grid Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" Width="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}" Height="{TemplateBinding ActualHeight}">
<Border x:Name="DropDownBorder" Background="White" BorderThickness="1" BorderBrush="#888888"/>
<ScrollViewer Margin="0,0,0,0" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="CornerRadius" Value="0"/>
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,0,0,0"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<!--<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>-->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ComboBox Style="{StaticResource CustomCombobox}" Focusable="False" x:Name="ComboBox" ItemsSource="{Binding Children}">
</ComboBox>
</Grid>
MultiButtonControl.xaml.cs
using CSIncludes.wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CSIncludes.controls
{
/// <summary>
/// Interaction logic for MultiButtonControl.xaml
/// </summary>
public partial class MultiButtonControl : UserControl
{
public MultiButtonControl()
{
InitializeComponent();
this.Loaded += UserControl_Loaded;
Children = new List<TestComboBoxItem>();
}
public static readonly DependencyProperty ParentButtonTextProperty = DependencyProperty.Register(
"ParentButtonText",
typeof(string),
typeof(MultiButtonControl));
public static readonly DependencyProperty ParentButtonImageProperty = DependencyProperty.Register(
"ParentButtonImage",
typeof(ImageSource),
typeof(AudioControl),
new UIPropertyMetadata(null));
public static readonly DependencyProperty ParentButtonHeightProperty = DependencyProperty.Register(
"ParentButtonHeight",
typeof(double),
typeof(MultiButtonControl));
public static readonly DependencyProperty ChildButtonHeightProperty = DependencyProperty.Register(
"ChildButtonHeight",
typeof(double),
typeof(MultiButtonControl));
public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register(
"Children",
typeof(List<TestComboBoxItem>),
typeof(MultiButtonControl));
public string ParentButtonText
{
get { return (string)GetValue(ParentButtonTextProperty); }
set { SetValue(ParentButtonTextProperty, value); }
}
public ImageSource ParentButtonImage
{
get { return (ImageSource)GetValue(ParentButtonImageProperty); }
set { SetValue(ParentButtonImageProperty, value); }
}
public double ParentButtonHeight
{
get { return (double)GetValue(ParentButtonHeightProperty); }
set { SetValue(ParentButtonHeightProperty, value); }
}
public double ChildButtonHeight
{
get { return (double)GetValue(ChildButtonHeightProperty); }
set { SetValue(ChildButtonHeightProperty, value); }
}
public List<TestComboBoxItem> Children
{
get { return (List<TestComboBoxItem>)GetValue(ChildrenProperty); }
set { SetValue(ChildrenProperty, value); }
}
void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (ParentButtonHeight == 0)
ParentButtonHeight = 35;
if (ChildButtonHeight == 0)
ChildButtonHeight = 25;
ComboBox.MaxDropDownHeight = ComboBox.Items.Count * ChildButtonHeight;
}
}
}
Generic.xaml
<Style x:Key="{x:Type wpf:TestComboBoxItem}" TargetType="{x:Type wpf:TestComboBoxItem}">
<Setter Property="ComboBoxItem.SnapsToDevicePixels" Value="true"/>
<Setter Property="ComboBoxItem.Foreground" Value="Black"/>
<Setter Property="ComboBoxItem.OverridesDefaultStyle" Value="true"/>
<Setter Property="ComboBoxItem.Height" Value="{Binding ChildButtonHeight}"/>
<Setter Property="ComboBoxItem.VerticalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TestComboBoxItem}">
<Border Name="Border" Padding="5" SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="#7E59F2"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TestComboBoxItem.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace CSIncludes.wpf
{
public class TestComboBoxItem : ComboBoxItem
{
static TestComboBoxItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TestComboBoxItem), new FrameworkPropertyMetadata(typeof(ComboBoxItem)));
}
public static DependencyProperty CommandParameter2Property =
DependencyProperty.Register("CommandParameter2", typeof(object), typeof(TestComboBoxItem));
public static DependencyProperty Command2Property =
DependencyProperty.Register("Command2", typeof(ICommand), typeof(TestComboBoxItem));
public static DependencyProperty ItemTextProperty =
DependencyProperty.Register("ItemText", typeof(string), typeof(TestComboBoxItem));
public ICommand Command2
{
get { return (ICommand)GetValue(Command2Property); }
set { SetValue(Command2Property, value); }
}
public object CommandParameter2
{
get { return GetValue(CommandParameter2Property); }
set { SetValue(CommandParameter2Property, value); }
}
public string ItemText
{
get { return (string)GetValue(ItemTextProperty); }
set { SetValue(ItemTextProperty, value); }
}
}
}
Do you have any clue on how to fix that TestComboBoxItem bindings ? How would you do to make it work? After binding the Content, I will need to create bindings with ICommand too.

xaml usercontrol multidatatrigger from parent control and itself (usercontrol)

So I'm trying to learn how to dynamically apply style changes to controls. I have not been able to get a user control to change its borderbrush and background based off a radio button in the main window and the usercontrol's text property. Basing it just off the usercontrol's text property does seem to work. So it appears that I'm doing something wrong with getting the radio button's isCheck property.
I've simplified from the original code but this still shows the issue.
MainWindow.xaml
<Window x:Class="UserControlTest.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:UserControlTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<RadioButton x:Name="calcPace" TabIndex="1" Content="Pace" HorizontalAlignment="Left" Margin="34,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
Height="16" Width="41"/>
<RadioButton x:Name="calcDistance" TabIndex="2" Content="Distance" HorizontalAlignment="Left" Margin="80,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
Height="16" Width="61"/>
<RadioButton x:Name="calcTime" TabIndex="3" Content="Time" HorizontalAlignment="Left" Margin="146,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
Height="16" Width="42"/>
<local:TextBoxTime/>
</Grid>
</Window>
TextBoxTime.xaml (usercontrol):
<UserControl x:Class="UserControlTest.TextBoxTime"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UserControlTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox x:Name="timeString" TabIndex="4" HorizontalAlignment="Left" Height="23" Margin="68,130,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="BorderBrush" Value="PaleGreen"/>
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=calcTime, Path=IsChecked}" Value="False"/>
<Condition Binding="{Binding ElementName=timeString, Path=Text}" Value=""/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="Background" Value="Snow"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
Currently I've added no code behind for either.
Thanks
Here's how I might do it:
public partial class RequireableTextBox : UserControl
{
public RequireableTextBox()
{
InitializeComponent();
}
#region IsRequired Property
public bool IsRequired
{
get { return (bool)GetValue(IsRequiredProperty); }
set { SetValue(IsRequiredProperty, value); }
}
public static readonly DependencyProperty IsRequiredProperty =
DependencyProperty.Register(nameof(IsRequired), typeof(bool), typeof(RequireableTextBox),
new PropertyMetadata(false));
#endregion IsRequired Property
#region Text Property
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(String), typeof(RequireableTextBox),
// Default must be "" not null, for the trigger to understand
new PropertyMetadata(""));
#endregion Text Property
}
XAML
<UserControl
x:Class="UserControlTest.RequireableTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UserControlTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
IsTabStop="False"
>
<Grid>
<TextBox
x:Name="timeString"
HorizontalAlignment="Left"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}, UpdateSourceTrigger=PropertyChanged}"
>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="BorderBrush" Value="PaleGreen"/>
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<!--
Seemed right to disable when unneeded; delete this trigger
if you'd rather not.
-->
<DataTrigger
Binding="{Binding IsRequired, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="False"
>
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding IsRequired, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="True"
/>
<Condition
Binding="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value=""
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="Background" Value="Snow"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</UserControl>
Usage:
<StackPanel>
<RadioButton x:Name="calcTime" GroupName="CalculationType">Calculate Time</RadioButton>
<RadioButton x:Name="calcDistance" GroupName="CalculationType">Calculate Distance</RadioButton>
<local:RequireableTextBox
IsRequired="{Binding IsChecked, ElementName=calcTime}"
/>
<local:RequireableTextBox
x:Name="DistanceValue"
IsRequired="{Binding IsChecked, ElementName=calcDistance}"
/>
<!-- Just tossed this in to demonstrate the Text property -->
<Label Content="{Binding Text, ElementName=DistanceValue}" Foreground="Gray" />
</StackPanel>

Gridview items as squares in WinRT (metro)

I've been struggeling with this a few days now and can't get it to work.
Maybe I'm not as good XAML programmer that I hoped I would be :)
Anyhow, my problem is that i want to bind a number of elements to a GridView and make them appear as squares without setting any width and height. The reason for this is that I want my GridView items to grow/shrink and expand to maximum size as the resolution or screen size vary.
Here is my XAML:
<UserControl.Resources>
<Style x:Key="MyItemContainerStyle" TargetType="ListViewItem">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<!--<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource Self}, Path=Width}" />-->
</Style>
<DataTemplate x:Key="MyItemTemplate">
<Border CornerRadius="4"
BorderBrush="Black"
BorderThickness="1"
Background="Blue">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">X</TextBlock>
</Border>
</DataTemplate>
<ItemsPanelTemplate x:Key="MyItemsPanelTemplate">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</UserControl.Resources>
<Grid Background="White">
<GridView x:Name="MyGrid"
UseLayoutRounding="True"
ItemTemplate="{StaticResource MyItemTemplate}"
ItemsPanel="{StaticResource MyItemsPanelTemplate}"
ItemContainerStyle="{StaticResource MyItemContainerStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
</GridView>
</Grid>
And this is my code-behind:
public sealed partial class BlankPage : Page
{
public BlankPage()
{
this.InitializeComponent();
MyGrid.Items.Add(new ListViewItem { Content = 1 });
MyGrid.Items.Add(new ListViewItem { Content = 2 });
MyGrid.Items.Add(new ListViewItem { Content = 3 });
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
This however produces an output like this (rectangular items, not squares):
I would appreciate if someone who knows a bit more about XAML and WinRT (metro) development than I do, could explain this for me and maybe give me a working example.
Thanx!
EDIT
I got a tip to wrap my Border in a Viewbox as it seems to have some scaling/stretching abilities.
I played around a couple of hours but I can't really get it to work 100%.
This is my XAML now:
<UserControl.Resources>
<Style x:Key="MyItemContainerStyle" TargetType="ListViewItem">
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
<DataTemplate x:Key="MyItemTemplate">
<Viewbox>
<Border CornerRadius="3"
BorderBrush="Black"
BorderThickness="1">
<Grid Background="Blue" MinHeight="50" MinWidth="50">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">X</TextBlock>
</Grid>
</Border>
</Viewbox>
</DataTemplate>
<ItemsPanelTemplate x:Key="MyItemsPanelTemplate">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
</StackPanel>
</ItemsPanelTemplate>
</UserControl.Resources>
<Grid Background="White">
<GridView x:Name="MyGrid"
UseLayoutRounding="True"
ItemTemplate="{StaticResource MyItemTemplate}"
ItemsPanel="{StaticResource MyItemsPanelTemplate}"
ItemContainerStyle="{StaticResource MyItemContainerStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled">
</GridView>
</Grid>
This produces this output:
Now it seems to stretch itself to a sqaure, but it sretches itself outside the screen. I also ran this example in several resoultions and screen sizes and it shows the same output, which means it scales correctly.
Help would be appreciated.
Thanx!
Your commented out binding is close to something that could sort of work - only you would need to bind to ActualWidth:
<Setter Property="Height" Value="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" />
Overall I would suggest using hardcoded values - they make things easier on the CPU to layout, easier to deterministically design and will get scaled by Windows when screen size and resolution will require that. If you want more control over that - you can bind both Width and Height to the same value from a view model that you change depending on the need or write a converter that will convert a hardcoded Width/Height value to actual value depending on detected screen size/resolution or settings.

Silverlight 4. How to set a ToolTip with controls in a ResourceDictionary file with Style definitions?

I've looked at tens of Q&As, but haven't found the answer to this apparently simple need.
I'm working with Silverlight 4. I want to define a ToolTip WITH CONTROLS IN IT at the ResourceDictionary file that has the Style definitions.
My user control file "UC_Activity.xaml" has:
...
<TextBox Style="{StaticResource Style0}" Name="tb_id" />
...
If my "Styles.xaml" file has
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Style x:Key="Style0" TargetType="TextBox">
<Setter Property="FontSize" Value="12" />
<Setter Property="FontFamily" Value="Portable User Interface" />
<Setter Property="ToolTipService.ToolTip" Value="Long tooltip text here. This WORKS, but part of the text ends up out of the screen." />
</Style>
</ResourceDictionary>
it works, but I can only show simple text as the ToolTip, and if the text is very long, it will end up out of the screen, where it is impossible to be seen. What I want is something like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Style x:Key="Style0" TargetType="TextBox">
<Setter Property="FontSize" Value="12" />
<Setter Property="FontFamily" Value="Portable User Interface" />
<Setter Property="ToolTipService.ToolTip">
<Setter.Value>
<StackPanel>
<sdk:Label Content="Short text here."/>
<TextBlock TextWrapping="Wrap" MaxWidth="200" Text="Long text here. This does NOT WORK." />
</StackPanel>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
but it does NOT WORK. It builds ok, but it gives an exception ("Value does not fall within the expected range.") when starting execution.
Please, how can I do it?
Thank you very much.
I had a similar issue, the problem is that it needs to be a DataTemplate so a different instance is added to the visual tree every time its used. I created an attached property for this:
then it can just be used as follows:
<Setter Property="ControlsBehaviours:TooltipTemplate.Template">
<Setter.Value>
<DataTemplate>
<ToolTip Content="tooltip" />
</DataTemplate>
</Setter.Value>
</Setter>
public class TooltipTemplate
{
/// <summary>
/// Template Dependency Property.
/// </summary>
public static readonly DependencyProperty TemplateProperty =
DependencyProperty.RegisterAttached(
"Template",
typeof (DataTemplate),
typeof (TooltipTemplate),
new PropertyMetadata(new PropertyChangedCallback(TemplateChanged)));
public static void SetTemplate(DependencyObject o, DataTemplate value)
{
o.SetValue(TemplateProperty, value);
}
public static DataTemplate GetTemplate(DependencyObject o)
{
return (DataTemplate) o.GetValue(TemplateProperty);
}
private static void TemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ToolTipService.SetToolTip(d, ((DataTemplate)e.NewValue).LoadContent());
}
}