Visual States and a custom dependency property (MVVM) - c#

I'm stuck trying to add a dependency property to a button. I have several buttons located in my header view and clicking on them changes the content in the ContentControl between different views. All this works great. I want the button that was clicked have a different forecolor than the others and it looks like I need to add a dependency property. I think I have all the pieces in place but can't figure out how to get them all to work together.
I have a string property named ViewState in my viewmodel which changes based upon the button being clicked. The property is changing and I'm calling RaisePropertyChanged when it happens. What do I need to do to bind the additional dependency property? I'm transitioning from the WinForm world and trying to mentally piece it all together but struggling a bit.
Here's what I have so far:
<Style TargetType="{x:Type Button}" x:Key="LocalButtonTemplate">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="18" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="outerBorder" Background="{TemplateBinding Background}" Margin="4">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ViewState">
<VisualState x:Name="Dashboard">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" To="Yellow"
Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="contentPresenter"/>
</Storyboard>
</VisualState>
<VisualState x:Name="AccountTables">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" To="Red"
Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="contentPresenter"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" To="Purple"
Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="contentPresenter"/>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" To="#35A84D"
Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="contentPresenter"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Border x:Name="Background" BorderBrush="Transparent">
<Grid>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="4,5,4,4"/>
</Grid>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My buttons:
<dxwuii:SplitPanel Margin="0,10,10,10" HorizontalAlignment="Right" Grid.Column="2" ItemSpacing="0" Orientation="Horizontal" ItemSizeMode="AutoSize" >
<Button Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="AccountTablesViewModel" Style="{StaticResource LocalButtonTemplate}">Express Tables</Button>
<Button Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="MappingViewModel" Style="{StaticResource LocalButtonTemplate}">Item Mapping</Button>
<Button Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="ReportsViewModel" Style="{StaticResource LocalButtonTemplate}">Reports</Button>
<Button Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="PostBalancesViewModel" Style="{StaticResource LocalButtonTemplate}">Post Balances</Button>
</dxwuii:SplitPanel>
Dependency Property Class:
namespace MyAppName.Model
{
public class StateManager : DependencyObject
{
public static string GetVisualStateProperty(DependencyObject obj)
{
return (string)obj.GetValue(VisualStatePropertyProperty);
}
public static void SetVisualStateProperty(DependencyObject obj, string value)
{
obj.SetValue(VisualStatePropertyProperty, value);
}
public static readonly DependencyProperty VisualStatePropertyProperty =
DependencyProperty.RegisterAttached(
"VisualStateProperty",
typeof(string),
typeof(StateManager),
new PropertyMetadata((dependencyObject, args) =>
{
var frameworkElement = dependencyObject as FrameworkElement;
if (frameworkElement == null)
return;
VisualStateManager.GoToState(frameworkElement, (string)args.NewValue, true);
}));
}
}

Set Tag and Attached property on your each button as below. Tag value will be the VisualState value to which button should go on click.
<Button Tag="AcountTables" local:StateManager.VisualStateProperty="{Binding YOURVIEWMODELPROPERTY}" Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="AccountTablesViewModel" Style="{StaticResource LocalButtonTemplate}">Express Tables</Button>
Update your AttachedProperty like:
public static readonly DependencyProperty VisualStatePropertyProperty =
DependencyProperty.RegisterAttached(
"VisualStateProperty",
typeof(string),
typeof(StateManager),
new PropertyMetadata((dependencyObject, args) =>
{
var frameworkElement = dependencyObject as FrameworkElement;
if (frameworkElement == null)
return;
if (args.NewValue == frameworkElement.Tag.ToString())
{
VisualStateManager.GoToState(frameworkElement, (string)args.NewValue, true);
}
else
{
VisualStateManager.GoToState(frameworkElement, "Normal", true);
}
}));

Related

UWP TreeView sort leads to random events

Team,
I run in a very weird issue with treeview.
Here are the steps to rep:
1. create a new project
2. select Window Studio Template
3. Select MVVM Light
4. Add treeview page
5. create project
6. Open TreeViewPage.xaml and add with:
xmlns:Custom="using:Microsoft.UI.Xaml.Controls"
xmlns:helper="using:TitoDoc2020.Helpers"
<Page.Resources>
<Style x:Key="RadioButtonRevealStyle" TargetType="RadioButton">
<Setter Property="Background" Value="{ThemeResource ToggleButtonRevealBackground}" />
<Setter Property="Foreground" Value="{ThemeResource ToggleButtonForeground}" />
<Setter Property="BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ToggleButtonRevealBorderThemeThickness}" />
<Setter Property="Padding" Value="{ThemeResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid
x:Name="RootGrid"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}">
<ContentPresenter
x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
CornerRadius="{TemplateBinding CornerRadius}" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="PointerOver" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundPointerOver}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushPointerOver}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundPointerOver}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="Pressed" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundPressed}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushPressed}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundPressed}" />
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundDisabled}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushDisabled}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundDisabled}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundChecked}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushChecked}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundChecked}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="CheckedPointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="PointerOver" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundCheckedPointerOver}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushCheckedPointerOver}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundCheckedPointerOver}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="CheckedPressed">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="Pressed" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundCheckedPressed}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushCheckedPressed}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundCheckedPressed}" />
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="CheckedDisabled">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundCheckedDisabled}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushCheckedDisabled}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundCheckedDisabled}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Indeterminate">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminate}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminate}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminate}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminatePointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="PointerOver" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminatePointerOver}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminatePointerOver}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminatePointerOver}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminatePressed">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="Pressed" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminatePressed}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminatePressed}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminatePressed}" />
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminateDisabled">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminateDisabled}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminateDisabled}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminateDisabled}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="treeViewColumn" MinWidth="150" MaxWidth="350" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!--641 is the default CompactModeThresholdWidth in NavigationView -->
<AdaptiveTrigger MinWindowWidth="641" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="header.Margin" Value="0,0,0,0" />
<Setter Target="treeViewColumn.Width" Value="350" />
<Setter Target="treeViewColumn.MaxWidth" Value="500" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid
Background="{ThemeResource SystemChromeMediumLowColor}">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid
Margin="80,0,0,0"
x:Name="header"
Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
x:Uid="TreeViewTitle"
Margin="{StaticResource SmallLeftMargin}"
Style="{StaticResource ListTitleStyle}"
VerticalAlignment="Center" />
<Button
Grid.Column="1"
x:Uid="TreeView_CollapseAllButton"
Content=""
FontSize="14"
Padding="{StaticResource SmallLeftRightMargin}"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
FontFamily="Segoe MDL2 Assets"
Command="{Binding ElementName=collapseBehavior, Path=CollapseAllCommand}"
Background="Transparent" />
</Grid>
<StackPanel
Grid.Row="1"
x:DefaultBindMode="OneWay"
Orientation="Horizontal">
<Custom:RadioButtons MaxColumns="2">
<RadioButton
x:Name="SortDate"
Width="50"
Height="50"
MinWidth="50"
Margin="{StaticResource ButtonPadding}"
Background="Transparent"
Click="{x:Bind ViewModel.RadioButton_ClickAsync}"
CornerRadius="2,2,2,2"
Style="{StaticResource RadioButtonRevealStyle}"
Checked="{x:Bind ViewModel.RadioButton_Checked}">
<RadioButton.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<FontIcon
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="2"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="32"
Glyph="" />
<FontIcon
Grid.Row="0"
Grid.Column="1"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<FontIcon
Grid.Row="1"
Grid.Column="1"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<FontIcon
Grid.Row="0"
Grid.Column="1"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<StackPanel Grid.Row="1" Grid.Column="0">
<FontIcon
Margin="0,6,0,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Foreground="{ThemeResource SystemAccentColorDark2}"
Glyph="{x:Bind ViewModel.ImageSrcD}" />
</StackPanel>
</Grid>
</RadioButton.Content>
</RadioButton>
<RadioButton
x:Name="SortAlph"
Width="50"
Height="50"
MinWidth="50"
Margin="{StaticResource ButtonPadding}"
Background="Transparent"
Click="{x:Bind ViewModel.RadioButton_ClickAsync}"
CornerRadius="2,2,2,2"
Style="{StaticResource RadioButtonRevealStyle}"
Checked="{x:Bind ViewModel.RadioButton_Checked}">
<RadioButton.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<FontIcon
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="2"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="32"
Glyph="" />
<FontIcon
Grid.Row="0"
Grid.Column="1"
Height="20"
VerticalAlignment="Top"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<FontIcon
Grid.Row="1"
Grid.Column="1"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<StackPanel Grid.Row="1" Grid.Column="0">
<FontIcon
Margin="0,6,0,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Foreground="{ThemeResource SystemAccentColorDark2}"
Glyph="{x:Bind ViewModel.ImageSrcA}" />
</StackPanel>
</Grid>
</RadioButton.Content>
</RadioButton>
</Custom:RadioButtons>
</StackPanel>
<winui:TreeView
x:Name="treeView"
Grid.Row="2"
SelectionMode="Single"
ItemsSource="{x:Bind ViewModel.SampleItems}"
ItemTemplateSelector="{StaticResource TreeViewTemplateSelector}">
<i:Interaction.Behaviors>
<behaviors:TreeViewCollapseBehavior x:Name="collapseBehavior" />
<ic:EventTriggerBehavior EventName="ItemInvoked">
<ic:InvokeCommandAction Command="{x:Bind ViewModel.ItemInvokedCommand}" />
</ic:EventTriggerBehavior>
</i:Interaction.Behaviors>
</winui:TreeView>
</Grid>
</Grid>
</Page>
Open TreeViewModel.cs and add replace with:
public string _sorting;
public bool _DateAsc;
public bool _AlphAsc;
public bool _SwapCheck = false;
public bool _filterData = false;
public int _filterDataSpan = 0;
public string ImageSrcD
{
get
{
if (_DateAsc)
{
return "\xF0AD";
}//SvgImageSource svgImage = new SvgImageSource(new Uri("ms-appx:///Assets/Image_16x.png"));
else
{
return "\xF0AE";
}
}
set { Set<bool>(ref _DateAsc, bool.Parse(value)); }
}
public string ImageSrcA
{
get
{
if (_AlphAsc)
{
return "\xF0AD";
}//SvgImageSource svgImage = new SvgImageSource(new Uri("ms-appx:///Assets/Image_16x.png"));
else
{
return "\xF0AE";
}
}
set { Set<bool>(ref _AlphAsc, bool.Parse(value)); }
}
public async Task LoadDataAsync()
{
var data = await SampleDataService.GetTreeViewDataAsync();
foreach (var item in data)
{
SampleItems.Add(item);
}
}
public async Task SortDataAsync()
{
var data = await SampleDataService.GetTreeViewDataAsync();
switch (_sorting)
{
default:
break;
case "SortAlph":
if (_AlphAsc)
{
data = data
.OrderBy(b => b.Country).ThenBy(b => b.CompanyName)
.ToList();
}
else
{
data = data
.OrderByDescending(b => b.Country).ThenByDescending(b => b.CompanyName)
.ToList();
}
break;
}
SelectedItem = null;
SampleItems.Clear();
foreach (var item in data)
{
SampleItems.Add(item);
}
}
public async void RadioButton_Checked(object sender, RoutedEventArgs e)
{
_SwapCheck = true;
/*string sorting = await ApplicationData.Current.LocalSettings.ReadAsync<string>("TreeViewSort");
switch (((RadioButton)sender).Name)
{
case "SortDate":
if (sorting == "SortDate")
{
return;
}
SortAlph.IsChecked = false;
break;
case "SortAlph":
if (sorting == "SortAlph")
{
return;
}
SortDate.IsChecked = false;
break;
default:
break;
}
ApplicationData.Current.LocalSettings.SaveAsync("TreeViewSort", ((ToggleButton)sender).Name);
_sorting = ((RadioButton)sender).Name;
SortTreeAsync();
//await LoadTreeAsync();
*/
}
public void RadioButton_ClickAsync(object sender, RoutedEventArgs e)
{
switch (((RadioButton)sender).Name)
{
case "SortAlph":
if (!_SwapCheck)
{
ImageSrcA = (!_AlphAsc).ToString();
}
break;
case "SortDate":
if (!_SwapCheck)
{
ImageSrcD = (!_DateAsc).ToString();
}
break;
default:
break;
}
_SwapCheck = false;
_sorting = ((RadioButton)sender).Name;
SortDataAsync();
//SortTreeAsync();
}
}
}
under Helpers folder create a class named TitoDoc2020Enums.cs paste:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TitoDoc2020.Helpers
{
public enum LoadingStatus
{
NotStarted = 0,
Loading = 1,
Loaded = 2
}
public enum VisualizationFormat
{
// Surname, Name
SurCName= 0,
// Surname Name
SurnName = 1,
// Name Surname
NameSur = 2
}
public enum Sorting
{
// Surname, Name
Surname = 0,
// Surname Name
Name = 1
}
public enum DateFilter
{
Day = 1, // 59583, // 
Week = 2, // 59584, // 
Month = 3, // 59271, // 
Year = 4, // // 
All = 0 //60041 // &#xEA89
}
}
Update all NuGet libraries and add compile
Click the right sorting icon multiple times, you can open some tree view items, everything happens... items get opened automatically, I even got the request to use the microphone
It does not seem to me I'm doing anything strange... why is so messed up?
nice to see you again, I could run this code sample and reproduce your problem easily. The problem is that you have not specific bool IsExpanded property for the model and bind it to TreeViewItem IsExpanded property. When you reorder the data current UI state will be covered by previous.
Please add the following to your model class and bind IsExpanded in xaml code.
public class SampleCompany : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return isExpanded; }
set
{
if (isExpanded != value)
{
isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
}
}
public class SampleOrder : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return isExpanded; }
set
{
if (isExpanded != value)
{
isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
}
}
Xaml code
<DataTemplate x:Key="CompanyTemplate" x:DataType="model:SampleCompany">
<winui:TreeViewItem
AutomationProperties.Name="{x:Bind CompanyName}"
IsExpanded="{x:Bind IsExpanded, Mode=OneWay}"
ItemsSource="{x:Bind Orders}">
<TextBlock Margin="{StaticResource XXSmallTopRightBottomMargin}" Text="{x:Bind CompanyName}" />
</winui:TreeViewItem>
</DataTemplate>
<DataTemplate x:Key="OrderTemplate" x:DataType="model:SampleOrder">
<winui:TreeViewItem
AutomationProperties.Name="{x:Bind OrderID}"
IsExpanded="{x:Bind IsExpanded, Mode=OneWay}"
ItemsSource="{x:Bind Details}">
<TextBlock Margin="{StaticResource XXSmallTopRightBottomMargin}" Text="{x:Bind ShortDescription}" />
</winui:TreeViewItem>
</DataTemplate>
Update
I've added the code but the Set part is never used and the Get part is read only when the parent is accessed
It's by-design, because, we use OneWay model, it will not edit the model property when you expand the tree view node. If we set it as TwoWay model set method will called.
BUT now the issue is that when I re-create the object I loose the status so they are all closed again.
When we re-create object but not specific IsExpanded value (default value is false),so all the nodes will close by default.

Unhandled exception `The parameter is incorrect` in NotifyCollectionChangedEvent handler

I have a very simple ListView whose ItemsSource is a ObservableCollection. Better show it with code:
MainPage.xaml:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Windows.UI.Xaml.Shapes"
x:Class="Test.MainPage" Background="Black" >
<Grid x:Name="Board" Background="Transparent" >
<ListView ItemsSource="{x:Bind LineList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Line">
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock Foreground="White" Text="{x:Bind Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Mainpage.xaml.cs:
public sealed partial class MainPage : Page
{
public ObservableCollection<Line> LineList = new ObservableCollection<Line>();
public MainPage()
{
InitializeComponent();
LineList.CollectionChanged += List_CollectionChanged;
LineList.Add(new Line { Name = "Line1" });
LineList.Add(new Line { Name = "Line2" });
}
private void List_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if(e.Action == NotifyCollectionChangedAction.Add)
{
Board.Children.Add(e.NewItems[0] as Line);//if I comment out this line, no exception
}
}
}
What I actually want is that, when I add a Line on the ListView to show it's Name, it be also added in the Grid as an actual Shape. Note that, I am using the ListView only to show the Names of those Lines, and in the Grid I want an actual Line Shape
I don't know what I've done wrong, but the above attempt gives the stated Exception.
If these informations help:
No Exception occurs if I don't add the Line in the Grid
No Exception if : Board.Children.Add(new Line { Name = "Line2" });
I've been fiddling around with your code and I was able to track down what is wrong with your code. However I'm not really sure why it's happening.
The reason why you're getting errors is because you're trying to use same instance of an UIElement (i.e. Line) that you're binding to your ListView.ItemsSource. Why it's failing, is a bit of mystery to me. I suspect that it's forbidden to Bind and add the same UIElement to XAML, as it might create binding loops!? That's just a wild guess though. Anyways...
You shouldn't be using UIElement as the binding context - I can't think of any scenario that you would do such thing. You will be better off by creating a separate model, as per my previous answer (e.g. LineViewModel), and using that as your BindingContext. Your MainPage.xaml.cs code could look like this:
public sealed partial class MainPage : Page
{
public ObservableCollection<LineViewModel> Lines = new ObservableCollection<LineViewModel>();
public MainPage()
{
InitializeComponent();
Lines.CollectionChanged += LinesOnCollectionChanged;
Lines.Add(new LineViewModel { Name = "Line1" });
Lines.Add(new LineViewModel { Name = "Line2" });
}
private void LinesOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
MainGrid.Children.Add(new Line()
{
Name = (e.NewItems[0] as LineViewModel)?.Name ?? string.Empty,
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 12,
X1 = 0,
X2 = 10000
});
}
}
}
public class LineViewModel
{
public string Name { get; set; }
}
The MainPage.xaml will stay the same, as per my previous answer
I'm not sure if this is what you're after but here it goes
MainPage.xaml
<Page x:Class="App4.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App4"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<Style x:Key="LineViewItemContainerStyle"
TargetType="ListViewItem">
<Setter Property="FontFamily"
Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize"
Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="Background"
Value="{ThemeResource ListViewItemBackground}" />
<Setter Property="Foreground"
Value="{ThemeResource ListViewItemForeground}" />
<Setter Property="TabNavigation"
Value="Local" />
<Setter Property="IsHoldingEnabled"
Value="True" />
<Setter Property="Padding"
Value="0" />
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="VerticalContentAlignment"
Value="Stretch" />
<Setter Property="MinWidth"
Value="{ThemeResource ListViewItemMinWidth}" />
<Setter Property="MinHeight"
Value="{ThemeResource ListViewItemMinHeight}" />
<Setter Property="AllowDrop"
Value="False" />
<Setter Property="UseSystemFocusVisuals"
Value="True" />
<Setter Property="FocusVisualMargin"
Value="0" />
<Setter Property="FocusVisualPrimaryBrush"
Value="{ThemeResource ListViewItemFocusVisualPrimaryBrush}" />
<Setter Property="FocusVisualPrimaryThickness"
Value="2" />
<Setter Property="FocusVisualSecondaryBrush"
Value="{ThemeResource ListViewItemFocusVisualSecondaryBrush}" />
<Setter Property="FocusVisualSecondaryThickness"
Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter x:Name="Root"
CheckBrush="{ThemeResource ListViewItemCheckBrush}"
ContentMargin="{TemplateBinding Padding}"
CheckBoxBrush="{ThemeResource ListViewItemCheckBoxBrush}"
ContentTransitions="{TemplateBinding ContentTransitions}"
CheckMode="{ThemeResource ListViewItemCheckMode}"
DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
DragBackground="{ThemeResource ListViewItemDragBackground}"
DragForeground="{ThemeResource ListViewItemDragForeground}"
FocusVisualSecondaryBrush="{TemplateBinding FocusVisualSecondaryBrush}"
FocusVisualPrimaryThickness="{TemplateBinding FocusVisualPrimaryThickness}"
FocusVisualSecondaryThickness="{TemplateBinding FocusVisualSecondaryThickness}"
FocusBorderBrush="{ThemeResource ListViewItemFocusBorderBrush}"
FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
FocusVisualPrimaryBrush="{TemplateBinding FocusVisualPrimaryBrush}"
FocusSecondaryBorderBrush="{ThemeResource ListViewItemFocusSecondaryBorderBrush}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Control.IsTemplateFocusTarget="True"
PressedBackground="{ThemeResource ListViewItemBackgroundPressed}"
PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackground}"
PointerOverForeground="{ThemeResource ListViewItemForegroundPointerOver}"
PointerOverBackground="{ThemeResource ListViewItemBackgroundPointerOver}"
ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
SelectedForeground="{ThemeResource ListViewItemForegroundSelected}"
SelectionCheckMarkVisualEnabled="{ThemeResource ListViewItemSelectionCheckMarkVisualEnabled}"
SelectedBackground="{ThemeResource ListViewItemBackgroundSelected}"
SelectedPressedBackground="{ThemeResource ListViewItemBackgroundSelectedPressed}"
SelectedPointerOverBackground="{ThemeResource ListViewItemBackgroundSelectedPointerOver}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected" />
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="PointerOver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOverSelected">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="PointerOver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOverPressed">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="Pressed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="Pressed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PressedSelected">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="Pressed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="DisabledStates">
<VisualState x:Name="Enabled" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="Root.RevealBorderThickness"
Value="0" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</ListViewItemPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid Background="Transparent">
<ListView ItemContainerStyle="{StaticResource LineViewItemContainerStyle}"
ItemsSource="{x:Bind Lines, Mode=OneTime}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:LineViewModel">
<Grid x:Name="ItemGrid">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Foreground="Black"
Text="{x:Bind Name, Mode=OneWay}" />
<Border Grid.Row="1"
BorderBrush="Black"
BorderThickness="1" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public ObservableCollection<LineViewModel> Lines = new ObservableCollection<LineViewModel>();
public MainPage()
{
InitializeComponent();
Lines.Add(new LineViewModel { Name = "Line1" });
Lines.Add(new LineViewModel { Name = "Line2" });
}
}
public class LineViewModel
{
public string Name { get; set; }
}
Note, that instead of using Line I used Border. Also, I needed to override base ListViewItemContainerStyle to set HorizontalContentAlignment and VerticalContentAlignment to Stretch, so that the DataTemplate elements can take up the entire space of the item
The result:

How to create a square button?

Thanks for #Justin XL and #grek40 help me so much.
I must apologize for my poor English that troubles everyone so much.
And I think I need to improve this question to help any others in the furture.
Here is the newest:
I need to make a square button like this:
My programme is a fullscreen programme that different device has different window's size.
So my square button should be can resizeable also beaucase I want to make a Reactive UI.
And now how can I make a square button?
Thank you.
It's perfectly fine to have pure UI logic like this live inside its code-behind. I'd even argue it's more efficient in most cases.
In your example, it's super easy to square your Rectangle with the following code
XAML
<Border x:Name="MyBorder"
Grid.Column="1"
Grid.Row="1"
SizeChanged="MyBorder_SizeChanged">
<Rectangle x:Name="MyRectangle"
Fill="LightBlue" />
</Border>
Code-behind
private void MyBorder_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (MyBorder.ActualWidth > MyBorder.ActualHeight)
{
MyRectangle.Width = MyRectangle.Height = MyBorder.ActualHeight;
}
else if (MyBorder.ActualWidth < MyBorder.ActualHeight)
{
MyRectangle.Height = MyRectangle.Height = MyBorder.ActualWidth;
}
}
But can we improve this? Since you want a square Button, it makes most sense to create a SquareButton and insert it straight into your Grid.
So the XAML can be simplified to a much more readable version below
<local:SquareButton Grid.Column="1" Grid.Row="1" />
Then you just need to implement the custom control like the following
SquareButton class
[TemplatePart(Name = PART_Root, Type = typeof(Border))]
[TemplatePart(Name = PART_ContentHost, Type = typeof(Border))]
public sealed class SquareButton : Button
{
private const string PART_Root = "Root";
private const string PART_ContentHost = "ContentHost";
public SquareButton()
{
DefaultStyleKey = typeof(SquareButton);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var root = (Border)GetTemplateChild(PART_Root);
var contentHost = (Border)GetTemplateChild(PART_ContentHost);
root.SizeChanged += (s, e) =>
{
if (root.ActualWidth > root.ActualHeight)
{
contentHost.Width = contentHost.Height = root.ActualHeight;
}
else if (root.ActualWidth < root.ActualHeight)
{
contentHost.Height = contentHost.Height = root.ActualWidth;
}
};
}
}
SquareButton Style inside Themes/Generic.xaml
<Style TargetType="local:SquareButton">
<Setter Property="Background"
Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
<Setter Property="Foreground"
Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="BorderBrush"
Value="{ThemeResource SystemControlForegroundTransparentBrush}" />
<Setter Property="BorderThickness"
Value="{ThemeResource ButtonBorderThemeThickness}" />
<Setter Property="Padding"
Value="8,4,8,4" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="VerticalAlignment"
Value="Stretch" />
<Setter Property="FontFamily"
Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight"
Value="Normal" />
<Setter Property="FontSize"
Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="UseSystemFocusVisuals"
Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SquareButton">
<Border x:Name="Root">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="ContentHost" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SystemControlHighlightBaseMediumLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SystemControlHighlightBaseHighBrush}" />
</ObjectAnimationUsingKeyFrames>
<PointerUpThemeAnimation Storyboard.TargetName="ContentHost" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentHost"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SystemControlBackgroundBaseMediumLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SystemControlHighlightBaseHighBrush}" />
</ObjectAnimationUsingKeyFrames>
<PointerDownThemeAnimation Storyboard.TargetName="ContentHost" />
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentHost"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SystemControlDisabledTransparentBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="ContentHost" Background="{TemplateBinding Background}">
<ContentPresenter x:Name="ContentPresenter"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTransitions="{TemplateBinding ContentTransitions}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw" />
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Hope this helps!
Use the Rectangle.Stretch property:
<Rectangle Fill="Red" Stretch="Uniform"></Rectangle>
I think this answers the actual question of creating a rectangle where width and height are the same and the rectangle is stretched to the available space.
In terms of binding, a MultiBinding on both Width and Height with an IMultiValueConverter implementation that returns the minimum of all input values might work. However, it's only needed for controls that don't provide automated stretching.
You can use attached properties to set the same width/height for a given limit:
public static class SquareSize
{
public static double GetWidthLimit(DependencyObject obj)
{
return (double)obj.GetValue(WidthLimitProperty);
}
public static void SetWidthLimit(DependencyObject obj, double value)
{
obj.SetValue(WidthLimitProperty, value);
}
public static readonly DependencyProperty WidthLimitProperty = DependencyProperty.RegisterAttached(
"WidthLimit", typeof(double), typeof(SquareSize),
new FrameworkPropertyMetadata(double.PositiveInfinity, new PropertyChangedCallback(OnWidthLimitChanged)));
private static void OnWidthLimitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UpdateSize(d, (double)e.NewValue, GetHeightLimit(d));
}
public static double GetHeightLimit(DependencyObject obj)
{
return (double)obj.GetValue(HeightLimitProperty);
}
public static void SetHeightLimit(DependencyObject obj, double value)
{
obj.SetValue(HeightLimitProperty, value);
}
public static readonly DependencyProperty HeightLimitProperty = DependencyProperty.RegisterAttached(
"HeightLimit", typeof(double), typeof(SquareSize),
new FrameworkPropertyMetadata(double.PositiveInfinity, new PropertyChangedCallback(OnHeightLimitChanged)));
private static void OnHeightLimitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UpdateSize(d, GetWidthLimit(d), (double)e.NewValue);
}
private static void UpdateSize(DependencyObject d, double widthLimit, double heightLimit)
{
double resultSize = Math.Min(widthLimit, heightLimit);
d.SetCurrentValue(FrameworkElement.WidthProperty, resultSize);
d.SetCurrentValue(FrameworkElement.HeightProperty, resultSize);
}
}
Use with appropriate xmlns namespace
<Border x:Name="border" Grid.Column="1" Grid.Row="1">
<Rectangle
Fill="Red"
local:SquareSize.WidthLimit="{Binding ElementName=border,Path=ActualWidth}"
local:SquareSize.HeightLimit="{Binding ElementName=border,Path=ActualHeight}"/>
</Border>
A solution involving a custom control as wrapper for square-spaced content:
public class SquareContentControl : ContentControl
{
protected override Size ArrangeOverride(Size arrangeBounds)
{
var sizeLimit = Math.Min(arrangeBounds.Width, arrangeBounds.Height);
if (VisualChildrenCount > 0)
{
var child = GetVisualChild(0) as UIElement;
if (child != null)
{
child.Arrange(new Rect(new Point((arrangeBounds.Width - sizeLimit) / 2, (arrangeBounds.Height - sizeLimit) / 2), new Size(sizeLimit, sizeLimit)));
return arrangeBounds;
}
}
return base.ArrangeOverride(arrangeBounds);
}
protected override Size MeasureOverride(Size constraint)
{
var sizeLimit = Math.Min(constraint.Width, constraint.Height);
if (VisualChildrenCount > 0)
{
var child = GetVisualChild(0) as UIElement;
if (child != null)
{
child.Measure(new Size(sizeLimit, sizeLimit));
return child.DesiredSize;
}
}
return base.MeasureOverride(constraint);
}
}
Usage:
<Border x:Name="border" Grid.Column="1" Grid.Row="1">
<local:SquareContentControl>
<Rectangle Fill="Red"/>
</local:SquareContentControl>
</Border>
EDIT 2017/8/17 only works on WPF, not UWP.
Using Minimum Converter:
public class MinConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double result = double.NaN;
if (values != null)
{
try
{
result = values.Cast<double>().Aggregate(double.PositiveInfinity, (a, b) => Math.Min(a, b));
}
catch (Exception)
{
result = double.NaN;
}
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Then in your xaml set the Rectangle Height to match parent's Border Min(ActualHeight, ActualWidth). And the Rectangle Width can just bind to Rectangle's ActualHeight
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.1*"></RowDefinition>
<RowDefinition Height="0.8*"></RowDefinition>
<RowDefinition Height="0.1*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.1*"></ColumnDefinition>
<ColumnDefinition Width="0.8*"></ColumnDefinition>
<ColumnDefinition Width="0.1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border x:Name="Bd" Grid.Column="1" Grid.Row="1">
<Rectangle x:Name="R"
Width="{Binding Path=ActualHeight, Mode=OneWay, RelativeSource={RelativeSource Self}}">
<Rectangle.Height>
<MultiBinding Converter="converter:MinConverter">
<Binding ElementName="Bd" Path="ActualHeight"/>
<Binding ElementName="Bd" Path="ActualWidth"/>
</MultiBinding>
</Rectangle.Height>
</Rectangle>
</Border>
</Grid>

WPF how to binding CornerRadius of Border inside button template

I have a UserControl which has a button inside it. This button has a personalized style as bellow. I want to create a property in my UserControl that affects the CornerRadius of a border called "Background" which is inside the button template, so that I can make the button corner round when needed.
I tried to create a property in my usercontrol and use OnApplyTemplate event and GetTemplateChild method but didn't work. I found the border, but nothing happens.
public override void OnApplyTemplate()
{
Border border = GetTemplateChild("Background") as Border;
border.CornerRadius = this.CornerRadius;
}
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
<DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
<ColorAnimation Duration="0" To="#8CFFFFFF" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="BackgroundGradient"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Background" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0" Background="Transparent" />
<Grid Background="{TemplateBinding Background}" Margin="1">
<Border x:Name="BackgroundAnimation" Background="Transparent" Opacity="0"/>
<Rectangle x:Name="BackgroundGradient" Fill="Transparent" />
</Grid>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Rectangle x:Name="DisabledVisualElement" Fill="#FFFFFFFF" IsHitTestVisible="false" Opacity="0" RadiusY="0" RadiusX="0"/>
<Rectangle x:Name="FocusVisualElement" IsHitTestVisible="false" Margin="1" Opacity="0" RadiusY="0" RadiusX="0" Stroke="#FF6DBDD1" StrokeThickness="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Edit - Solution
I got what I wanted by creating a heritage of Button with a dependency property for CornerRadius, then using TemplateBinding.
Important XAML parts:
<Style x:Key="ButtonStyle" TargetType="myProject:MyButton">
<ControlTemplate TargetType="myProject:MyButton">
<Border x:Name="Background" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}">
Full XAML and C#
public class MyButton : Button
{
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.Register("CornerRadius", typeof(CornerRadius),
typeof(MyButton), new FrameworkPropertyMetadata(new CornerRadius(0, 0, 0, 0)));
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
}
<Style x:Key="ButtonStyle" TargetType="myProject:MyButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="myProject:MyButton">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
<DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
<ColorAnimation Duration="0" To="#8CFFFFFF" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="BackgroundGradient"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Background" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}">
<Grid>
<Border x:Name="BackgroundAnimation" Background="Transparent" Opacity="0"/>
<Rectangle x:Name="BackgroundGradient" Fill="Transparent"/>
</Grid>
</Border>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Rectangle x:Name="DisabledVisualElement" Fill="#FFFFFFFF" IsHitTestVisible="false" Opacity="0" RadiusY="{Binding ElementName=Background, Path=CornerRadius.TopLeft}" RadiusX="{Binding ElementName=Background, Path=CornerRadius.BottomLeft}"/>
<Rectangle x:Name="FocusVisualElement" IsHitTestVisible="false" Margin="1" Opacity="0" RadiusX="0" RadiusY="0" Stroke="#FF6DBDD1" StrokeThickness="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The best way to do that through Template Binding. WPF best practices say that you need to apply UI changes through appropriate WPF mechanism: XAML. Consequently, we need to change our Style or Template. If it is possible, we should create a flexible template to allow to change UI by changing of style, creation a new Style without modification of the Template.
Open/Closed SOLID principle: Extend and not modify.
Unfortunately, we forgot that CornerRadius it is the attached property of class Border.
So we can create a Template Binding:
<ControlTemplate TargetType="{x:Type Button}"
x:Key="ControlTemplateButtonNormal">
<Border Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="{TemplateBinding Border.CornerRadius}"
x:Name="BorderRoot">
<Grid>
<ContentPresenter IsTabStop="False"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextElement.Foreground="{TemplateBinding Foreground}" />
</Grid>
</Border>
<Trigger Property="Validation.HasError"
Value="True">
<Setter Property="Visibility"
TargetName="ErrorElement"
Value="Visible" />
<Setter Property="BorderBrush"
TargetName="BorderRoot"
Value="Transparent" />
</Trigger>
<Trigger Property="IsReadOnly"
Value="True">
<Setter Property="Background"
TargetName="BorderRoot"
Value="{StaticResource BorderBrushReadonly}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Now we can use this template to create different Styles.
Normal button:
<Style TargetType="{x:Type Button}"
x:Key="ButtonNormalStyle">
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Border.CornerRadius"
Value="0" />
<Setter Property="Template"
Value="{StaticResource ControlTemplateButtonNormal}" />
</Style>
Button Round:
<Style TargetType="{x:Type Button}"
x:Key="ButtonRoundStyle">
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Border.CornerRadius"
Value="5,5,5,5" />
<Setter Property="Template"
Value="{StaticResource ControlTemplateButtonNormal}" />
</Style>
I hope it will be useful.
If you have a property called CornerRadius in your control, I'm pretty sure this would do it:
<Border CornerRadius="{TemplateBinding CornerRadius}"/>
You need to search the VisualTree for the border. it's not part of the UserControl's template so it's not a Template child.
Here's some good extensions to help you with this and other situations where you need to traverse the VisualTree :
public static class VisualTreeHelperExtensions
{
public static T FindVisualParent<T>(DependencyObject depObj) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(depObj);
if (parent == null || parent is T)
return (T)parent;
return FindVisualParent<T>(parent);
}
public static T FindVisualChild<T>(DependencyObject depObj) where T : Visual
{
if (depObj != null && IsVisual(depObj))
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
return childOfChild;
}
}
}
return null;
}
public static T FindVisualChild<T>(DependencyObject depObj, string name) where T : FrameworkElement
{
if (depObj != null && IsVisual(depObj))
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T && (child as T).Name.Equals(name))
{
return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
if (childOfChild.Name.Equals(name))
return childOfChild;
}
}
}
return null;
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null)
yield break;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
if (IsVisual(depObj))
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T) child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
private static bool IsVisual(DependencyObject depObj)
{
return depObj is Visual || depObj is Visual3D;
}
}
In this case use :
public override void OnApplyTemplate()
{
Border border = VisualTreeHelperExtensions.FindVisualChild<Border>(this,"Background") as Border;
border.CornerRadius = this.CornerRadius;
}

Auto-complete box with drop-down

The AutoCompleteBox is pretty great, but one feature it lacks is a click-to-drop-down all the available options. You can come close by setting MinimumPrefixLength=0 -- that way, the user can get the complete drop-down list by deleting the text. This has a couple of limitations, though:
if there is no text to begin with, then the user would have to enter some text and delete it (non-intuitive and inconvenient)
the click-and-keypress sequence seems like suboptimal UX.
How would you tweak this control to make it drop down the complete list of options, when the user clicks on the control (or, also fine, a button to the right)?
It seems I have disappointed #HighCore. So far I tried adding a button to the control template, which triggers opening the Popup. The drawback to this approach is that, if there is text entered, then the list will be filtered per the normal filtering rules. Now, you could clear the text, thus removing the filter, but this has another side effect: de-selecting the currently selected item (in contrast to a ComboBox, whose drop-down you can open without de-selecting). So ... what now? Temporarily remove the filter, and restore it when the popup is closed or the user types anything else?
I built a control like that, the approach I used was to inherit from the AutoComboBox control and remove the filter when the dropdown opens.
EDIT
Code has been updated to include missing resources.
Code is:
public class AutoCompleteComboBox : AutoCompleteBox
{
/// <summary>
/// Occurs when drop down toggle button is clicked.
/// </summary>
public event EventHandler ToggleButtonClick;
private object _holdSelectedItem;
private AutoCompleteFilterMode _holdFilterMode;
/// <summary>
/// Identifies the DisplayMemberPath dependency property.
/// </summary>
public static readonly DependencyProperty DisplayMemberPathProperty = DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(AutoCompleteComboBox), new PropertyMetadata(string.Empty, DisplayMemberPath_PropertyChanged));
/// <summary>
/// Gets or sets the name or path of the property
/// that is displayed for each the data item in the control.
/// </summary>
public string DisplayMemberPath
{
get { return (string)GetValue(DisplayMemberPathProperty); }
set { SetValue(DisplayMemberPathProperty, value); }
}
private static void DisplayMemberPath_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var accb = (AutoCompleteComboBox)d;
accb.ValueMemberPath = e.NewValue.ToString();
}
/// <summary>
/// Identifies the MaxLength dependency property.
/// </summary>
public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register("MaxLength", typeof(int), typeof(AutoCompleteComboBox), null);
/// <summary>
/// Gets or sets the maximum number of characters allowed for user input.
/// </summary>
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
/// <summary>
/// Gets or sets a collection used to generate the content of the control.
/// </summary>
public new IEnumerable ItemsSource
{
get { return GetValue(ItemsSourceProperty) as IEnumerable; }
set
{
SetValue(SelectedItemProperty, null);
SetValue(ItemsSourceProperty, value);
Dispatcher.BeginInvoke(() => SetValue(TextProperty, string.Empty));
_holdSelectedItem = null;
}
}
/// <summary>
/// Initializes a new instance of the AutoCompleteComboBox control.
/// </summary>
public AutoCompleteComboBox()
{
StreamResourceInfo sri = Application.GetResourceStream(new Uri("/UI.Controls;component/AutoCompleteComboBox.xaml", UriKind.Relative));
var sr = new StreamReader(sri.Stream);
Style = (Style)XamlReader.Load(sr.ReadToEnd());
DropDownClosed += AutoCompleteComboBox_DropDownClosed;
DropDownOpened += AutoCompleteComboBox_DropDownOpened;
}
/// <summary>
/// Called when the template's tree is generated.
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var toggle = (ToggleButton)GetTemplateChild("PopupToggleButton");
toggle.Click += DropDownToggle_Click;
var lb = (ListBox)GetTemplateChild("Selector");
lb.DisplayMemberPath = DisplayMemberPath;
_holdFilterMode = FilterMode;
TextChanged += AutoCompleteComboBox_TextChanged;
SelectionChanged += new SelectionChangedEventHandler(AutoCompleteComboBox_SelectionChanged);
}
private void AutoCompleteComboBox_DropDownClosed(object sender, RoutedPropertyChangedEventArgs<bool> e)
{
if (SelectedItem == null && _holdSelectedItem != null && SelectedItem != _holdSelectedItem && ItemsSource.Cast<object>().Contains(_holdSelectedItem))
{
SelectedItem = _holdSelectedItem;
}
_holdSelectedItem = null;
FilterMode = _holdFilterMode;
}
private void AutoCompleteComboBox_DropDownOpened(object sender, RoutedPropertyChangedEventArgs<bool> e)
{
var lb = (ListBox)GetTemplateChild("Selector");
ScrollViewer sv = lb.GetScrollHost();
if (sv != null)
{
sv.ScrollToTop();
}
if (SelectedItem != null)
{
lb.SelectedItem = SelectedItem;
_holdSelectedItem = SelectedItem;
}
}
private void DropDownToggle_Click(object sender, RoutedEventArgs e)
{
IsDropDownOpen = !IsDropDownOpen;
if (ToggleButtonClick != null)
{
ToggleButtonClick(this, e);
}
if (IsDropDownOpen)
{
_holdFilterMode = FilterMode;
FilterMode = AutoCompleteFilterMode.None;
((TextBox)GetTemplateChild("Text")).SelectAll();
}
Focus();
}
private void AutoCompleteComboBox_TextChanged(object sender, RoutedEventArgs e)
{
if (IsDropDownOpen)
{
if (FilterMode == AutoCompleteFilterMode.None && FilterMode != _holdFilterMode)
{
FilterMode = _holdFilterMode;
}
ScrollViewer sv = ((ListBox)GetTemplateChild("Selector")).GetScrollHost();
if (sv != null)
{
sv.ScrollToTop();
}
}
}
private void AutoCompleteComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!IsDropDownOpen && SelectedItem == null)
{
Text = string.Empty;
}
}
}
<Setter Property="Height" Value="24" />
<Setter Property="MinimumPopulateDelay" Value="1" />
<Setter Property="IsTextCompletionEnabled" Value="False" />
<Setter Property="MinimumPrefixLength" Value="0" />
<Setter Property="MaxDropDownHeight" Value="300" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Padding" Value="2" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="#FF000000" />
<Setter Property="Background" Value="#FFFFFFFF" />
<Setter Property="Foreground" Value="#FF000000" />
<Setter Property="MinWidth" Value="45" />
<Setter Property="FilterMode" Value="Contains" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ctrls:AutoCompleteComboBox">
<Grid >
<Grid.Resources>
<Style x:Name="comboToggleStyle" TargetType="ToggleButton">
<Setter Property="Foreground" Value="#FF333333"/>
<Setter Property="Background" Value="#FF1F3B53"/>
<Setter Property="BorderBrush">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFA3AEB9" Offset="0"/>
<GradientStop Color="#FF8399A9" Offset="0.375"/>
<GradientStop Color="#FF718597" Offset="0.375"/>
<GradientStop Color="#FF617584" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="3"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundOverlay" Storyboard.TargetProperty="Opacity" To="1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#7FFFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#CCFFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="#F2FFFFFF"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundOverlay2" Storyboard.TargetProperty="Opacity" To="1"/>
<DoubleAnimation Duration="0" Storyboard.TargetName="Highlight" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="#E5FFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#BCFFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#6BFFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" To="#F2FFFFFF"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundOverlay3" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
<DoubleAnimation Duration="0" Storyboard.TargetName="Highlight" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
<DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundGradient2" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient2" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="#E5FFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient2" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#BCFFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient2" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#6BFFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient2" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" To="#F2FFFFFF"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked"/>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Visibility" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="Background" RadiusX="3" RadiusY="3" Fill="{TemplateBinding Background}" StrokeThickness="{TemplateBinding BorderThickness}" Stroke="{TemplateBinding BorderBrush}"/>
<Rectangle x:Name="BackgroundOverlay" Opacity="0" RadiusX="3" RadiusY="3" Fill="#FF448DCA" StrokeThickness="{TemplateBinding BorderThickness}" Stroke="#00000000"/>
<Rectangle x:Name="BackgroundOverlay2" Opacity="0" RadiusX="3" RadiusY="3" Fill="#FF448DCA" StrokeThickness="{TemplateBinding BorderThickness}" Stroke="#00000000"/>
<Rectangle x:Name="BackgroundGradient" RadiusX="2" RadiusY="2" StrokeThickness="1" Margin="{TemplateBinding BorderThickness}" Stroke="#FFFFFFFF">
<Rectangle.Fill>
<LinearGradientBrush StartPoint=".7,0" EndPoint=".7,1">
<GradientStop Color="#FFFFFFFF" Offset="0" />
<GradientStop Color="#F9FFFFFF" Offset="0.375" />
<GradientStop Color="#E5FFFFFF" Offset="0.625" />
<GradientStop Color="#C6FFFFFF" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle Opacity="0" x:Name="BackgroundOverlay3" RadiusX="3" RadiusY="3" Fill="#FF448DCA" StrokeThickness="{TemplateBinding BorderThickness}" Stroke="#00000000"/>
<Rectangle Opacity="0" x:Name="BackgroundGradient2" RadiusX="2" RadiusY="2" StrokeThickness="1" Margin="{TemplateBinding BorderThickness}" Stroke="#FFFFFFFF">
<Rectangle.Fill>
<LinearGradientBrush StartPoint=".7,0" EndPoint=".7,1">
<GradientStop Color="#FFFFFFFF" Offset="0" />
<GradientStop Color="#F9FFFFFF" Offset="0.375" />
<GradientStop Color="#E5FFFFFF" Offset="0.625" />
<GradientStop Color="#C6FFFFFF" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="Highlight" RadiusX="2" RadiusY="2" Opacity="0" IsHitTestVisible="false" Stroke="#FF6DBDD1" StrokeThickness="1" Margin="{TemplateBinding BorderThickness}" />
<ContentPresenter
x:Name="contentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="{TemplateBinding Padding}"/>
<Rectangle x:Name="FocusVisualElement" RadiusX="3.5" Margin="1" RadiusY="3.5" Stroke="#FF6DBDD1" StrokeThickness="1" Visibility="Collapsed" IsHitTestVisible="false" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="CommonValidationToolTipTemplate" TargetType="ToolTip">
<Grid x:Name="Root" Margin="5,0" RenderTransformOrigin="0,0" Opacity="0">
<Grid.RenderTransform>
<TranslateTransform x:Name="Translation" X="-25" />
</Grid.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="OpenStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
<VisualTransition To="Open" GeneratedDuration="0:0:0.2">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Translation" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BackEase Amplitude=".3" EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.2" />
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Closed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0" Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Open">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Translation" Storyboard.TargetProperty="X" To="0" Duration="0" />
<DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Margin="4,4,-4,-4" Background="#052A2E31" CornerRadius="5" />
<Border Margin="3,3,-3,-3" Background="#152A2E31" CornerRadius="4" />
<Border Margin="2,2,-2,-2" Background="#252A2E31" CornerRadius="3" />
<Border Margin="1,1,-1,-1" Background="#352A2E31" CornerRadius="2" />
<Border Background="#FFDC000C" CornerRadius="2">
<TextBlock UseLayoutRounding="false" Foreground="White" Margin="8,4,8,4" MaxWidth="250" TextWrapping="Wrap" Text="{Binding (Validation.Errors)[0].ErrorContent}" />
</Border>
</Grid>
</ControlTemplate>
</Grid.Resources>
<TextBox x:Name="Text" VerticalContentAlignment="Center" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Foreground="{TemplateBinding Foreground}" MaxLength="{TemplateBinding MaxLength}" />
<ToggleButton x:Name="PopupToggleButton" Width="24" HorizontalAlignment="Right" Style="{StaticResource comboToggleStyle}" Margin="1" >
<Path x:Name="BtnArrow" Height="4" Width="8" Stretch="Uniform" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z " HorizontalAlignment="Center">
<Path.Fill>
<SolidColorBrush x:Name="BtnArrowColor" Color="#FF333333"/>
</Path.Fill>
</Path>
</ToggleButton>
<Border x:Name="ValidationErrorElement" Visibility="Collapsed" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1">
<ToolTipService.ToolTip>
<ToolTip x:Name="validationTooltip" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource CommonValidationToolTipTemplate}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<ToolTip.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsHitTestVisible">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<system:Boolean>true</system:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToolTip.Triggers>
</ToolTip>
</ToolTipService.ToolTip>
<Grid Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12" Background="Transparent">
<Path Fill="#FFDC000C" Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" />
<Path Fill="#ffffff" Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8" />
</Grid>
</Border>
<Popup x:Name="Popup">
<ListBox x:Name="Selector" SelectionMode="Single" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="{TemplateBinding MaxDropDownHeight}" />
</Popup>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PopupStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0" To="PopupOpened" />
<VisualTransition GeneratedDuration="0:0:0" To="PopupClosed" />
</VisualStateGroup.Transitions>
<VisualState x:Name="PopupOpened">
</VisualState>
<VisualState x:Name="PopupClosed">
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid" />
<VisualState x:Name="InvalidUnfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="InvalidFocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsOpen">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<system:Boolean>True</system:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
I worked out one approach. The basic idea is to overlay a button, either in a modified control template, or just flung on top of the view (I'll use the latter here for brevity's sake). The button thing does three things:
Clear the text (in order to restore the complete unfiltered list)
Set IsDropDownOpen = true to open the Popup
Restore the selected item to the ListBox template child
So basically, I have a setup like this:
<Grid>
<mycontrols:ExtendedAutoCompleteBox x:Name="autoCompleteBox" ... />
<Button HorizontalAlignment="Right" Click="Button_Click">
<Path Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z "
Fill="Black" Stretch="Uniform"
Width="8" Height="4"
/>
</Button>
</Grid>
The purpose of subclassing AutoCompleteBox is just to get access to the template child:
public class ExtendedAutoCompleteBox : AutoCompleteBox
{
private ListBox _listBox;
public override void OnApplyTemplate()
{
_listBox = GetTemplateChild("Selector") as ListBox;
}
public void ShowAllOptions()
{
var selectedItem = SelectedItem;
Text = "";
IsDropDownOpen = true;
_listBox.SelectedItem = selectedItem;
UpdateLayout();
_listBox.ScrollIntoView(selectedItem);
}
}
And so the button click will just call this "ShowAllOptions":
private void Button_Click(object sender, RoutedEventArgs e)
{
autoCompleteBox.ShowAllOptions();
}
It's not perfect -- for some reason the keyboard controls get screwy after clicking the button (if you press enter on an item, it does not select the item, but clears it instead) -- but it is better than nothing.
So just to illustrate what it does: let's say you have an item "x" selected, and you click the arrow button, then you will see the complete list:

Categories