I have a tree which represents XML document. And I show XML validations errors using a bound class which implements INotifyDataErrorInfo.
It works all fine on editable controls like text box or combo.
But some nodes are just text blocks. And they have errors too.
How do I go about it?
I recommend you wrap your TextBlock in a ValidationErrorBorder:
<ValidationErrorBorder DataContextValidationTargetPath="MyTextProperty">
<TextBlock Text="{Binding Path=MyTextProperty}"/>
</ValidationErrorBorder>
ValidationErrorBorder control code:
/*
* Use in conjuction with other Controls that do not have the appropriate
* visualStates to indicate ValidationErrors.
* Either set DataContextValidationTargetPath to have this
* ValidationErrorBorder show validation errors that occur at the targeted
* property of the DataContext
* or bind ValidationTarget to any target you wish to have this
* ValidationErrorBorder show validation errors occuring for the bound target.
*/
[TemplateVisualState(GroupName="ValidationStates", Name = "Valid")]
[TemplateVisualState(GroupName="ValidationStates", Name = "InvalidUnfocused")]
[TemplateVisualState(GroupName="ValidationStates", Name = "InvalidFocused")]
public class ValidationErrorBorder : ContentControl
{
public object ValidationTarget
{
get { return GetValue( ValidationTargetProperty ); }
set { SetValue( ValidationTargetProperty, value ); }
}
public static readonly DependencyProperty ValidationTargetProperty =
DependencyProperty.Register( "ValidationTarget", typeof( object ),
typeof( ValidationErrorBorder ), new PropertyMetadata( null ) );
public string DataContextValidationTargetPath
{
get
{
return (string) GetValue( DataContextValidationTargetPathProperty );
}
set { SetValue( DataContextValidationTargetPathProperty, value ); }
}
public static readonly DependencyProperty
DataContextValidationTargetPathProperty =
DependencyProperty.Register( "DataContextValidationTargetPath",
typeof( string ), typeof( ValidationErrorBorder ),
new PropertyMetadata( null, HandlePathChanged ) );
private static void HandlePathChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
((ValidationErrorBorder) d).HandlePathChanged();
}
private void HandlePathChanged()
{
if (DataContextValidationTargetPath != null)
SetBinding(ValidationTargetProperty,
new Binding(DataContextValidationTargetPath));
else
ClearValue( ValidationTargetProperty );
}
public ValidationErrorBorder()
{
DefaultStyleKey = typeof( ValidationErrorBorder );
}
}
and the control template:
<Style TargetType="controls:ValidationErrorBorder">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:ValidationErrorBorder">
<Grid x:Name="RootElement" Background="{x:Null}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid"/>
<VisualState x:Name="InvalidUnfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="InvalidFocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" Storyboard.TargetName="validationTooltip">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<system:Boolean>True</system:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" />
<Border x:Name="ValidationErrorElement" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1" Visibility="Collapsed">
<ToolTipService.ToolTip>
<ToolTip x:Name="validationTooltip" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource ValidationToolTipTemplate}">
<ToolTip.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="validationTooltip">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<system:Boolean>True</system:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToolTip.Triggers>
</ToolTip>
</ToolTipService.ToolTip>
<Grid Background="Transparent" HorizontalAlignment="Right" Height="12" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12">
<Path Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C" Margin="1,3,0,0"/>
<Path Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff" Margin="1,3,0,0"/>
</Grid>
</Border>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and the validationTooltipTemplate:
<ControlTemplate x:Key="ValidationToolTipTemplate">
<Grid x:Name="Root" Margin="5,0" Opacity="0" RenderTransformOrigin="0,0">
<Grid.RenderTransform>
<TranslateTransform x:Name="xform" X="-25"/>
</Grid.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="OpenStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
<VisualTransition GeneratedDuration="0:0:0.2" To="Open">
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" To="0" Storyboard.TargetProperty="X" Storyboard.TargetName="xform">
<DoubleAnimation.EasingFunction>
<BackEase Amplitude=".3" EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Duration="0:0:0.2" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Closed">
<Storyboard>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Open">
<Storyboard>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="X" Storyboard.TargetName="xform"/>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Background="#052A2E31" CornerRadius="5" Margin="4,4,-4,-4"/>
<Border Background="#152A2E31" CornerRadius="4" Margin="3,3,-3,-3"/>
<Border Background="#252A2E31" CornerRadius="3" Margin="2,2,-2,-2"/>
<Border Background="#352A2E31" CornerRadius="2" Margin="1,1,-1,-1"/>
<Border Background="#FFDC000C" CornerRadius="2"/>
<Border CornerRadius="2">
<TextBlock Foreground="White" MaxWidth="250" Margin="8,4,8,4" TextWrapping="Wrap" Text="{Binding (Validation.Errors)[0].ErrorContent}" UseLayoutRounding="false"/>
</Border>
</Grid>
</ControlTemplate>
It is not really supported in Silverlight by default. One way that I have found to work well in the past is to make a control that inherits from Textblock. You can then handle the IDatanotify interface and exceptions and turn the textblock different colors based off of those.
Related
How can i make such alist of checkboxes, that they are left aligned to the button, from where it opens, and provide a opening and closing Animation, like in the Alarm & Clock App in Windows 10? I tried with Popups and Flyouts, but there isnt the same effect.
It looks that this Control in the Alarm & Clock App is actually expanded from a ComboBox control.
So we can use a ComboBox to do this, for example:
<ComboBox x:Name="comboBox" VerticalAlignment="Center" HorizontalAlignment="Stretch" ItemsSource="{x:Bind comboitems}"
PlaceholderText="{x:Bind contenttext, Mode=OneWay}" DropDownClosed="comboBox_DropDownClosed" SelectionChanged="comboBox_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding dayofweek}" IsChecked="{Binding ischecked, Mode=TwoWay}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
To show the selected days on the ComboBox is a little complex, we can do this work in the code behind:
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private ObservableCollection<comboItem> comboitems = new ObservableCollection<comboItem>();
private string _contenttext;
public string contenttext
{
get
{
return _contenttext;
}
set
{
if (value != _contenttext)
{
_contenttext = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private int daycount;
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
showdays();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
comboitems.Clear();
comboitems.Add(new comboItem { dayofweek = "Sunday", ischecked = false });
comboitems.Add(new comboItem { dayofweek = "Monday", ischecked = true });
comboitems.Add(new comboItem { dayofweek = "Tuesday", ischecked = true });
comboitems.Add(new comboItem { dayofweek = "Wednesday", ischecked = true });
comboitems.Add(new comboItem { dayofweek = "Thursday", ischecked = true });
comboitems.Add(new comboItem { dayofweek = "Friday", ischecked = true });
comboitems.Add(new comboItem { dayofweek = "Saturday", ischecked = false });
}
private void comboBox_DropDownClosed(object sender, object e)
{
showdays();
}
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
comboBox.SelectedIndex = -1;
}
private void showdays()
{
contenttext = null;
daycount = 0;
for (int i = 0; i < comboBox.Items.Count(); i++)
{
comboItem item = comboBox.Items.ElementAt(i) as comboItem;
if (item.ischecked)
{
contenttext = contenttext + item.dayofweek.Substring(0, 3) + ", ";
daycount++;
}
}
if (daycount != 0)
{
if (daycount == 2 && contenttext == "Sun, Sat, ")
{
contenttext = "Weekends";
}
else if (daycount == 5 && contenttext == "Mon, Tue, Wed, Thu, Fri, ")
{
contenttext = "Weekdays";
}
else if (daycount == 7)
{
contenttext = "Every day";
}
else
{
contenttext = contenttext.TrimEnd(' ', ',');
}
}
else
{
contenttext = "Only once";
}
}
}
And the comboItem class by my side is like this:
public class comboItem : INotifyPropertyChanged
{
public string dayofweek { get; set; }
private bool _ischecked;
public bool ischecked
{
get
{
return _ischecked;
}
set
{
if (value != _ischecked)
{
_ischecked = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Maybe you can find another easier way to convert days to specific string, eventually my method works here.
Update:
Here is the whole modified template style of the ComboBox, you can also edit the template by your self.
<Style TargetType="ComboBox">
<Setter Property="Padding" Value="12,5,0,7" />
<Setter Property="MinWidth" Value="{ThemeResource ComboBoxThemeMinWidth}" />
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="Background" Value="{ThemeResource SystemControlBackgroundAltMediumLowBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumLowBrush}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="TabNavigation" Value="Once" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto" />
<Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="True" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<CarouselPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlPageBackgroundAltMediumBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseMediumBrush}" />
</ObjectAnimationUsingKeyFrames>
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextBlock">
<DiscreteObjectKeyFrame KeyTime="0" Value="LightBlue" />
</ObjectAnimationUsingKeyFrames>-->
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundListMediumBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseMediumLowBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="HeaderContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextBlock">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DropDownGlyph">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="HighlightBackground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HighlightBackground" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextBlock">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DropDownGlyph">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseMediumHighBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="FocusedPressed">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HighlightBackground" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextBlock">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DropDownGlyph">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseMediumHighBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
<VisualState x:Name="PointerFocused" />
<VisualState x:Name="FocusedDropDown">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="PopupBorder">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="DropDownStates">
<VisualState x:Name="Opened">
<Storyboard>
<SplitOpenThemeAnimation ClosedTargetName="ContentPresenter" OffsetFromCenter="{Binding TemplateSettings.DropDownOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}" OpenedTargetName="PopupBorder" OpenedLength="{Binding TemplateSettings.DropDownOpenedHeight, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</Storyboard>
</VisualState>
<VisualState x:Name="Closed">
<Storyboard>
<SplitCloseThemeAnimation ClosedTargetName="ContentPresenter" OffsetFromCenter="{Binding TemplateSettings.DropDownOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}" OpenedTargetName="PopupBorder" OpenedLength="{Binding TemplateSettings.DropDownOpenedHeight, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="HeaderContentPresenter" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" FontWeight="{ThemeResource ComboBoxHeaderThemeFontWeight}" FlowDirection="{TemplateBinding FlowDirection}" Margin="{ThemeResource ComboBoxHeaderThemeMargin}" Visibility="Collapsed" x:DeferLoadStrategy="Lazy" />
<Border x:Name="Background" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" Grid.Row="1" />
<Border x:Name="HighlightBackground" BorderBrush="{ThemeResource SystemControlHighlightBaseMediumLowBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{ThemeResource SystemControlHighlightListAccentLowBrush}" Grid.ColumnSpan="2" Opacity="0" Grid.Row="1" />
<ContentPresenter x:Name="ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Grid.Row="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Grid.ColumnSpan="2">
<TextBlock x:Name="PlaceholderTextBlock" Foreground="{ThemeResource SystemControlForegroundAccentBrush}" Text="{TemplateBinding PlaceholderText}" />
</ContentPresenter>
<!--<FontIcon x:Name="DropDownGlyph" AutomationProperties.AccessibilityView="Raw" Grid.Column="1" Foreground="{ThemeResource SystemControlForegroundBaseMediumHighBrush}" FontSize="12" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="" HorizontalAlignment="Right" IsHitTestVisible="False" Margin="0,10,10,10" Grid.Row="1" VerticalAlignment="Center" />-->
<Popup x:Name="Popup">
<Border x:Name="PopupBorder" BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}" BorderThickness="{ThemeResource ComboBoxDropdownBorderThickness}" Background="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" HorizontalAlignment="Stretch" Margin="0,-1,0,-1">
<ScrollViewer x:Name="ScrollViewer" AutomationProperties.AccessibilityView="Raw" BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}" Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" MinWidth="{Binding TemplateSettings.DropDownContentMinWidth, RelativeSource={RelativeSource Mode=TemplatedParent}}" VerticalSnapPointsType="OptionalSingle" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" VerticalSnapPointsAlignment="Near" ZoomMode="Disabled">
<ItemsPresenter Margin="{ThemeResource ComboBoxDropdownContentMargin}" />
</ScrollViewer>
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It's just remove the FontIcon inside this control and make the TextBlock's ColumnSpan = 2, and change it's foreground to SystemControlForegroundAccentBrush. If you want to modify it by your self, you can open the Document OutLine, find this ComboBox, right click on it, then choose Edit Template then Edit a Copy, so you will get the default template of ComboBox:
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:
I am making a C# wpf application with Visual Studio 2012. Here is the toast like notification. It is animated to go off by 4 seconds. It I get the mouse over this I want to pause the animation.
How can I achieve that ?
<Window x:Class="Exmaple.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
Title="Notification Popup" Width="300" SizeToContent="Height"
WindowStyle="None" AllowsTransparency="True" Height="Auto" Background="Transparent">
<Grid x:Name="abc" RenderTransformOrigin="0,1" Height="Auto" Width="300" Margin="0,0,0,0" MouseEnter="Grid_MouseEnter_1" >
<!-- Notification area -->
<Border BorderThickness="1" Background="#FF2D2D30" BorderBrush="Black" CornerRadius="0" Margin="0,0,0,0">
<!--StackPanel Margin="20"-->
<TextBlock x:Name="textblocknotify" TextWrapping="Wrap" Height="Auto" Margin="5" Foreground="White">
</TextBlock>
<!--CheckBox Content="Checkable" Margin="5 5 0 5" /-->
<!--Button Content="Clickable" HorizontalAlignment="Center" /-->
<!--/StackPanel-->
</Border>
<!-- Animation -->
<Grid.Triggers >
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard Completed="Storyboard_Completed_1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:4.5" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard Completed="Storyboard_Completed_1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:4.5" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<ScaleTransform ScaleY="1" />
</Grid.RenderTransform>
</Grid>
Code behind this
public partial class Window1 : Window
{
public Window1(String s)
{
InitializeComponent();
textblocknotify.Text = s;
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
var workingArea = System.Windows.SystemParameters.WorkArea;
var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
var corner = transform.Transform(new Point(workingArea.Right, workingArea.Bottom));
this.Left = corner.X - this.ActualWidth - 10;
this.Top = corner.Y - this.ActualHeight-30;
}));
//this.Close();
}
private void Storyboard_Completed_1(object sender, EventArgs e)
{
this.Close();
}
private void Grid_MouseEnter_1(object sender, MouseEventArgs e)
{
//don't know what to do
}
You can use the PauseStoryboard Class and the UIElement.MouseEnter Event to pause a running Animation. Equally, if you want the Animation to resume when the mouse is no longer over the control, then you can use the ResumeStoryboard Class and the UIElement.MouseLeave Event. Here is a simple example to demonstrate:
<Button Content="Click Me">
<Button.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Name="OpacityStoryboard">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)"
From="0" To="1" RepeatBehavior="Forever" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<PauseStoryboard BeginStoryboardName="OpacityStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<ResumeStoryboard BeginStoryboardName="OpacityStoryboard" />
</EventTrigger>
</Button.Triggers>
</Button>
I have an animation that animates a Canvas by turning it 360 degrees indefinitely (it basically spins). What I want is for this animation to start when the control is shown and then stop when the control is hidden. I figured I could tie this in, somehow, to the VisualStateManager. I have seen an example of fading in and out controls here which could work but I just dont know how to use VSM to start and stop the storyboard
<Canvas.Resources>
<Storyboard x:Name="spinnerBoard">
<DoubleAnimation
Storyboard.TargetName="SpinnerRotate"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:01.3"
RepeatBehavior="Forever" />
</Storyboard>
</Canvas.Resources>
<Canvas.RenderTransform>
<RotateTransform x:Name="SpinnerRotate" Angle="0" />
</Canvas.RenderTransform>
Example VSM
<VisualState x:Name="Show">
<Storyboard>
<!-- Start the story board here -->
</Storyboard>
</VisualState>
<VisualState x:Name="Hide">
<Storyboard>
<!-- Stop the story board here -->
</Storyboard>
</VisualState>
A global answer of your different questions :
ExtendedVisualStateManager.GoToElementState returns false in Silverlight
Default binding to UserControl for custom DP
You can do something like this :
Use a template control that extend ContentControl to play with
IsEnabled of content (prevent action during waiting) ;
Create a DP IsWaiting that switch your control visual state ;
Create the two states in XAML : Use DoubleAnimation with RepeatBehavior="Forever"
After you can add a overlay and a Waiting message dependency property like the busy indicator control...
I use a picture for the Waiting visual part but you can use a canvas, grid etc...
C#
[TemplateVisualState(GroupName = "WaitGroup", Name = WaitSpinner.IsWaitingStateName)]
[TemplateVisualState(GroupName = "WaitGroup", Name = WaitSpinner.NotWaitingStateName)]
public class WaitSpinner : ContentControl
{
#region States names
internal const String IsWaitingStateName = "IsWaitingState";
internal const String NotWaitingStateName = "NotWaitingState";
#endregion States names
public bool IsWaiting
{
get { return (bool)GetValue(IsWaitingProperty); }
set { SetValue(IsWaitingProperty, value); }
}
public static readonly DependencyProperty IsWaitingProperty =
DependencyProperty.Register("IsWaiting", typeof(bool), typeof(WaitSpinner), new PropertyMetadata(false, OnIsWaitingPropertyChanged));
private static void OnIsWaitingPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
WaitSpinner waitSpinner = (WaitSpinner)sender;
waitSpinner.ChangeVisualState(true);
}
public WaitSpinner()
{
DefaultStyleKey = typeof(WaitSpinner);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ChangeVisualState(false);
}
protected virtual void ChangeVisualState(bool useTransitions)
{
VisualStateManager.GoToState(this, IsWaiting ? IsWaitingStateName : NotWaitingStateName, useTransitions);
}
}
Xaml :
<VisualStateGroup x:Name="WaitGroup">
<VisualState x:Name="NotWaitingState" >
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.IsEnabled)" Storyboard.TargetName="content">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Boolean>True</System:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="IsWaitingState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="WaitPart">
<DiscreteObjectKeyFrame KeyTime="0:0:0.200" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" Storyboard.TargetName="WaitPart" To="360" RepeatBehavior="Forever" Duration="0:0:1" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.IsEnabled)" Storyboard.TargetName="content">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Boolean>False</System:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<!-- ............. -->
<ContentControl
IsTabStop="False"
x:Name="content"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Foreground="{TemplateBinding Foreground}"
ScrollViewer.HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
ScrollViewer.VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"/>
<Image Source="CirclePicture.png"
x:Name="WaitPart"
RenderTransformOrigin="0.5,0.5"
Width="16"
Height="16"
Visibility="Collapsed"
IsHitTestVisible="False">
<Image.RenderTransform>
<RotateTransform />
</Image.RenderTransform>
</Image>
How to add labels with sum for column. I need to do that with chart stacked series from silverlight toolkit
Was created custom StackedColumnSeriesEx!
Here you should find define that we are using new custom data points type and we can find sum of datapoints values. Also we should define which datapoint is located TOP most in StackedColumnSeries:
public class StackedColumnSeriesEx : StackedColumnSeries
{
protected override DataPoint CreateDataPoint()
{
// Custom data point with new fields.
return new CustomDataPoint();
}
protected override void UpdateDataItemPlacement(IEnumerable<DataItem> dataItems)
{
// Calculate sum here.
foreach (var group in this.IndependentValueGroups)
{
decimal sum = 0;
foreach (var dataItem in group.DataItems)
{
double currentValue = 0;
if (ValueHelper.TryConvert(dataItem.ActualDependentValue, out currentValue))
{
sum += Convert.ToDecimal(currentValue);
}
}
// Set sum and find most top point
foreach (DataItem dataItem in group.DataItems)
{
int index = group.DataItems.IndexOf(dataItem);
var convertedDataItem = dataItem.DataPoint as CustomDataPoint;
if (convertedDataItem == null)
{
continue;
}
convertedDataItem.SeriesDefinition = dataItem.SeriesDefinition;
convertedDataItem.IsTopPoint = index + 1 == group.DataItems.Count();
convertedDataItem.DependentValueSum = sum;
}
}
base.UpdateDataItemPlacement(dataItems);
}
}
New CustomDataPoint with few new dependency properies
IsTopPoint - bool value indicating whether current point is top most point!
DependentValueSum - double value to display. Dependency property
New style of CustomDataPoint to draw text DependentValueSum in case if its most top value:
`
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomDataPoint">
<Grid x:Name="Root"
Opacity="0">
<ToolTipService.ToolTip>
<ToolTip BorderThickness="0"
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Background="Transparent"
BorderBrush="Transparent">
<ToolTip.Template>
<ControlTemplate TargetType="ToolTip">
<Charts:ExpensesToolTip HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
</ControlTemplate>
</ToolTip.Template>
</ToolTip>
</ToolTipService.ToolTip>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.1" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.1" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Unselected" />
<VisualState x:Name="Selected" />
</VisualStateGroup>
<VisualStateGroup x:Name="RevealStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.5" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Shown">
<Storyboard>
<DoubleAnimation Duration="0"
To="1"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="Root" />
</Storyboard>
</VisualState>
<VisualState x:Name="Hidden">
<Storyboard>
<DoubleAnimation Duration="0"
To="0"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="Root" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups><TextBlock Text="{TemplateBinding DependentValuesSum}"
VerticalAlignment="Top" Margin="-2,-21,0,0" TextAlignment="Center"
Visibility="{Binding IsTopPoint, Converter={StaticResource VisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Center" />
<Grid x:Name="grid"
Background="{TemplateBinding Background}">
<Grid.OpacityMask>
<LinearGradientBrush EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="#87FFFFFF" />
<GradientStop Color="#D6FFFFFF"
Offset="1" />
</LinearGradientBrush>
</Grid.OpacityMask>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>`