I'm trying to create a download bar like chrome.
The issue I'm currently having is trying to bind the click event to the button's context menu within the listboxitem. When the context menuitem is clicked, it says the action is not found.
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Button BorderBrush="Transparent" BorderThickness="0" telerik:StyleManager.Theme="Windows8" Click="ButtonBase_OnClick">
<StackPanel Name="Panel" SnapsToDevicePixels="True"
Orientation="Horizontal" Margin="1 0"
Height="30">
<ContentControl Margin="0 0 10 0" Height="20">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate" Value="{StaticResource Icons.File}"></Setter>
</Style>
</ContentControl.Style>
</ContentControl>
<TextBlock Foreground="Black" Text="{Binding FileName}"
VerticalAlignment="Center"
TextAlignment="Center"
Margin="1 0 0 0"/>
<Button x:Name="ExpandButton" Background="Transparent" Click="ExpandButton_OnClick" BorderThickness="0" VerticalAlignment="Center" ContextMenuService.IsEnabled="false">
<Button.ContextMenu>
<ContextMenu x:Name="popup">
<MenuItem Header="Open" cal:Message.Attach="[Click] = [Open($this)]"></MenuItem>
</ContextMenu>
</Button.ContextMenu>
<ContentControl ContentTemplate="{StaticResource Icons.ArrowUp}" Width="10" Height="10" Margin="2" VerticalAlignment="Center"/>
</Button>
<Rectangle Width="2" Fill="Gray" Margin="0 0 0 0"/>
</StackPanel>
</Button>
</ControlTemplate>
I could bind it behind code(xaml.cs) side of the application but I also lose track of what item is the context suppose to point to. To do that, i replaced caliburn's click event with a regular Click event. The SelectedItem and SelectedItems is null or empty, respectively.
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
var originalSource = e.OriginalSource;
var selectedItem = FileListBox.SelectedItem;
var SelectedItems = FileListBox.SelectedItems;
}
Haven't tested but something along these lines should open the context menu on right or left click:
<Button x:Name="ExpandButton" Background="Transparent" Click="ContextMenu_Click" BorderThickness="0" VerticalAlignment="Center" ContextMenuService.IsEnabled="false">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu x:Name="popup" MenuItem.Click="menuItem_Click">
<MenuItem Header="Open" cal:Message.Attach="[Click] = [Open($this)]"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
<ContentControl ContentTemplate="{StaticResource Icons.ArrowUp}" Width="10" Height="10" Margin="2" VerticalAlignment="Center"/>
</Button>
As for the code-behind, the following worked for me in my last tug with a similar issue:
DependencyObject mainDep = new DependencyObject();
private void ContextMenu_Click(object sender, RoutedEventArgs e)
{
DependencyObject dep = (DependencyObject)e.OriginalSource;
while ((dep != null) && !(dep is ListBoxItem))
{
dep = VisualTreeHelper.GetParent(dep);
}
mainDep = dep;
}
private void menuItem_Click(object sender, RoutedEventArgs e)
{
DependencyObject dep = mainDep;
if (dep is ListBoxItem)
{
...
DO your stuff here
...
}
}
Let me know how these work for you
Related
So I've been playing around with creating a custom title bar using WPF and this is what I have
<!--Add the WindowChrome object-->
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="34" />
</WindowChrome.WindowChrome>
<Window.Resources>
<ResourceDictionary>
<Style x:Key="CaptionButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="LayoutRoot" Background="Transparent" Width="44" Height="30">
<TextBlock x:Name="txt" Text="{TemplateBinding Content}" FontFamily="Segoe MDL2 Assets" FontSize="10"
Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center"
RenderOptions.ClearTypeHint="Auto" TextOptions.TextRenderingMode="Aliased" TextOptions.TextFormattingMode="Display"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="LayoutRoot" Property="Background" Value="#E5E5E5"/>
<Setter TargetName="txt" Property="Foreground" Value="#000000"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--Minimize-->
<Style x:Key="MinimizeButtonStyle" TargetType="Button" BasedOn="{StaticResource CaptionButtonStyle}">
<Setter Property="Content" Value=""/>
</Style>
<!--Maximize-->
<Style x:Key="MaximizeButtonStyle" TargetType="Button" BasedOn="{StaticResource CaptionButtonStyle}">
<Setter Property="Content" Value=""/>
</Style>
<!--Restore-->
<Style x:Key="RestoreButtonStyle" TargetType="Button" BasedOn="{StaticResource CaptionButtonStyle}">
<Setter Property="Content" Value=""/>
</Style>
<!--Close-->
<Style x:Key="CloseButtonStyle" TargetType="Button" BasedOn="{StaticResource CaptionButtonStyle}">
<Setter Property="Content" Value=""/>
</Style>
</ResourceDictionary>
</Window.Resources>
<!-- Title bar button commands -->
<Window.CommandBindings>
<CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Close" />
<CommandBinding Command="{x:Static SystemCommands.MaximizeWindowCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Maximize" />
<CommandBinding Command="{x:Static SystemCommands.MinimizeWindowCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Minimize" />
<CommandBinding Command="{x:Static SystemCommands.RestoreWindowCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Restore" />
</Window.CommandBindings>
<Border x:Name="MainWindowBorder" BorderBrush="LightCoral" BorderThickness="0" >
<Grid x:Name="parentContainer" Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height ="Auto"/>
<RowDefinition Height ="*"/>
</Grid.RowDefinitions>
<!--Window chrome-->
<Grid Grid.Row="0" Height="30" Background="#F999">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
<!--App icon-->
<Image Source="/Resources/watermelon.ico" Width="18" Margin="2" HorizontalAlignment="Left" VerticalAlignment="Center" />
<TextBlock Text="Sweet App" FontFamily="Arial" Margin="4 3 0 0" />
</StackPanel>
<!--Caption buttons-->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
<Button Style="{StaticResource MinimizeButtonStyle}" WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Minimize"
Command="{x:Static SystemCommands.MinimizeWindowCommand}"/>
<Button x:Name="RestoreButton" Visibility="Collapsed" Style="{StaticResource RestoreButtonStyle}"
Command="{x:Static SystemCommands.RestoreWindowCommand}" WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Restore"/>
<Button x:Name="MaximizeButton" Visibility="Visible" Style="{StaticResource MaximizeButtonStyle}"
Command="{x:Static SystemCommands.MaximizeWindowCommand}" WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Maximize" />
<Button Style="{StaticResource CloseButtonStyle}" WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Close"
Command="{x:Static SystemCommands.CloseWindowCommand}"/>
</StackPanel>
</Grid>
<!--App content-->
<Grid Grid.Row="1" x:Name="AppArea">
<Path Data="M50,0L100,50 50,100 0,50z" Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2" />
</Grid>
</Grid>
</Border>
And that works great!
However if I were to comment out this part
<Window.CommandBindings>
<CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Close" />
<CommandBinding Command="{x:Static SystemCommands.MaximizeWindowCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Maximize" />
<CommandBinding Command="{x:Static SystemCommands.MinimizeWindowCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Minimize" />
<CommandBinding Command="{x:Static SystemCommands.RestoreWindowCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Restore" />
</Window.CommandBindings>
Then when I hover over the buttons (Minimize, Maximize and Close) they don't change color anymore and there are no obvious errors presented to me. And as far as I know, it shouldn't change anything UI related since the actual trigger for changing the button background is already defined above in the Window.Resources
Why is this happening?
Here's the code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
StateChanged += MainWindowStateChangeRaised;
}
// Can execute
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
// Minimize
private void CommandBinding_Executed_Minimize(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.MinimizeWindow(this);
}
// Maximize
private void CommandBinding_Executed_Maximize(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.MaximizeWindow(this);
}
// Restore
private void CommandBinding_Executed_Restore(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.RestoreWindow(this);
}
// Close
private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.CloseWindow(this);
}
// State change
private void MainWindowStateChangeRaised(object sender, EventArgs e)
{
if (WindowState == WindowState.Maximized)
{
MainWindowBorder.BorderThickness = new Thickness(8);
RestoreButton.Visibility = Visibility.Visible;
MaximizeButton.Visibility = Visibility.Collapsed;
}
else
{
MainWindowBorder.BorderThickness = new Thickness(0);
RestoreButton.Visibility = Visibility.Collapsed;
MaximizeButton.Visibility = Visibility.Visible;
}
}
}
I would like to slide the text (marquee text) of the selected item in a combobox, if it's lenght is bigger than the width of the combobox. It can be either automatical or when the user put the mouse over the combobox. The problem is that i have absolutely no idea on how to do that. It's maybe possible to do that with a render transform (previous definition of a textblock inside it)? or with a storyboard?
Here is the xaml that i need to modify
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate" >
<Setter.Value>
<DataTemplate DataType="DataGridColumnHeader" >
<ComboBox ItemContainerStyle="{StaticResource SingleSelectionComboBoxItem}" DisplayMemberPath="Oggetto" Width="100" Height="20" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},Path=DataContext.Selezione, UpdateSourceTrigger=LostFocus}" SelectionChanged="SingleSelectionComboBox_SelectionChanged"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
EDIT: the problem is that i don't know which properties should i target in the storyboard
EDIT2: i took the template of the combobox and modified the text box section like this :
<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0" To="100" Duration="00:00:10" Storyboard.TargetProperty="X" Storyboard.TargetName="transferCurreny" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform x:Name="transferCurreny" X="0"/>
</Setter.Value>
</Setter>
The problem is that this seems to have no effect
EDIT 3: i realized that i had to use the template that use the style i mentioned above
<ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="Placement" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2"
IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
MinWidth="{Binding ActualWidth, ElementName=Placement}">
<Border x:Name="DropDownBorder"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"
BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect"
Fill="{Binding Background, ElementName=DropDownBorder}"
Height="{Binding ActualHeight, ElementName=DropDownBorder}"
Width="{Binding ActualWidth, ElementName=DropDownBorder}" />
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter"
KeyboardNavigation.DirectionalNavigation="Contained"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<Themes:ListBoxChrome x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" Grid.ColumnSpan="2"
RenderMouseOver="{TemplateBinding IsMouseOver}"
RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" />
<TextBox x:Name="PART_EditableTextBox"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"
Margin="{TemplateBinding Padding}" Style="{StaticResource ComboBoxEditableTextBox}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" >
</TextBox>
<ToggleButton Grid.Column="1"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ComboBoxToggleButton}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="Foreground" Value="Black" />
</Trigger>
<Trigger Property="IsDropDownOpen" Value="true">
<Setter Property="RenderFocused" TargetName="Border" Value="true" />
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="DropDownBorder" Value="95" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
<Setter Property="Background" Value="#FFF4F4F4" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true" />
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</MultiTrigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5" />
<Setter Property="Color" TargetName="Shdw" Value="#71000000" />
</Trigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="OpaqueRect"
Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}" />
<Setter Property="Canvas.Left" TargetName="OpaqueRect"
Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
the textbox section is the one that use the style. However when i do Template="{StaticResource ComboBoxEditableTemplate}" in the combobox, i cannot see the selected item anymore, even if it's not null
Updated 08/02
Basically in order to avoid the animation issues you are facing - you will need to add a ScrollViewer as a wrapper to the content-site or selection-box TextBlock. You can either do that by using a custom template selector for your ComboBox so as defined below, or provide a custom item-template with ScollViewer.
Secondly, you can either create a custom ComboBox control in C# to bind the above template-selector/item-template and apply slide-animation on MouseEnterEvent; or implement the same in XAML itself.
For this example - have implemented the control in C#, and provided
a sample usage in XAML.
Code for custom control, and template selector
public class SlidingComboBox : ComboBox
{
public static readonly DependencyProperty SlideForeverProperty = DependencyProperty.Register("SlideForever", typeof(bool), typeof(SlidingComboBox), new FrameworkPropertyMetadata(false));
public bool SlideForever
{
get { return (bool)GetValue(SlideForeverProperty); }
set { SetValue(SlideForeverProperty, value); }
}
protected ContentPresenter _parent;
protected DoubleAnimation _animation;
protected TranslateTransform _translate;
protected Storyboard _storyBoard;
public SlidingComboBox()
{
Loaded += ExComboBox_Loaded;
ClipToBounds = true;
//assign template selector - just to re-template ContentSite / selection box
//uncomment this code - if you want to default-template for popup-items
//ItemTemplateSelector = new SlidingComboBoxItemTemplateSelector();
}
private void ExComboBox_Loaded(object sender, RoutedEventArgs e)
{
Loaded -= ExComboBox_Loaded;
//get content-site holder/parent
_parent = this.GetChildOfType<ContentPresenter>();
//setup slide animation
_animation = new DoubleAnimation()
{
From = 0,
RepeatBehavior = SlideForever ? RepeatBehavior.Forever : new RepeatBehavior(1), //repeat only if slide-forever is true
AutoReverse = SlideForever
};
//create storyboard
_storyBoard = new Storyboard();
_storyBoard.Children.Add(_animation);
Storyboard.SetTargetProperty(_animation, new PropertyPath("RenderTransform.(TranslateTransform.X)"));
}
protected override void OnMouseEnter(MouseEventArgs e)
{
//get actual textblock that renders the selected value
var textBlock = _parent.GetChildOfType<TextBlock>();
//and translate-transform for animation
textBlock.RenderTransform = _translate = new TranslateTransform();
//start animation only if text-block width is greater than parent
if (_parent.ActualWidth < textBlock.ActualWidth)
{
_animation.Duration = TimeSpan.FromMilliseconds(((int)textBlock.Text?.Length * 100));
_animation.To = _parent.ActualWidth - textBlock.ActualWidth;
_storyBoard.Begin(textBlock);
}
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
//stop animation once mouse pointer is off the control
_storyBoard.Stop();
//reset render state
var textBlock = _parent.GetChildOfType<TextBlock>();
textBlock.RenderTransform = _translate = new TranslateTransform();
base.OnMouseLeave(e);
}
}
public class SlidingComboBoxItemTemplateSelector : DataTemplateSelector
{
DataTemplate _selectedItemTemplate;
public SlidingComboBoxItemTemplateSelector()
{
//create datatemplate with ScrollViewer and TextBlock as child
var textBlock = new FrameworkElementFactory(typeof(TextBlock));
textBlock.SetValue(TextBlock.TextWrappingProperty, TextWrapping.NoWrap);
textBlock.SetBinding(TextBlock.TextProperty, new Binding("SelectedValue")
{
RelativeSource = new RelativeSource { Mode = RelativeSourceMode.FindAncestor, AncestorType = typeof(ComboBox) }
});
var scrollViewer = new FrameworkElementFactory(typeof(ScrollViewer));
scrollViewer.SetValue(ScrollViewer.CanContentScrollProperty, true);
scrollViewer.SetValue(ScrollViewer.HorizontalScrollBarVisibilityProperty, ScrollBarVisibility.Hidden);
scrollViewer.SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Disabled);
scrollViewer.AppendChild(textBlock);
_selectedItemTemplate = new DataTemplate
{
VisualTree = scrollViewer
};
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
ComboBoxItem comboBoxItem = container.GetVisualParent<ComboBoxItem>();
if (comboBoxItem == null)
{
//send back only if template requested for ContentSite, and not for combo-box item(s)
return _selectedItemTemplate;
}
return null;
}
}
/// <summary>
/// VisualTree helper
/// </summary>
public static class HelperExtensions
{
public static T GetChildOfType<T>(this DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
public static T GetVisualParent<T>(this DependencyObject child) where T : Visual
{
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
}
Sample usage:
<Window x:Class="MarqueeSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MarqueeSample"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="400">
<Grid Margin="20">
<DataGrid AutoGenerateColumns="False" IsReadOnly="True" Width="225" VerticalAlignment="Center">
<DataGrid.ItemsSource>
<col:Hashtable>
<col:ArrayList x:Key="TestData1">
<sys:String>Tiny</sys:String>
<sys:String>Keep calm and love programming</sys:String>
</col:ArrayList>
<col:ArrayList x:Key="TestData2">
<sys:String>Sample string</sys:String>
<sys:String>Another string to test</sys:String>
</col:ArrayList>
</col:Hashtable>
</DataGrid.ItemsSource>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="100" Binding="{Binding Key}" />
<DataGridTemplateColumn Header="Value" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:SlidingComboBox ItemsSource="{Binding Value}">
<local:SlidingComboBox.ItemTemplate>
<DataTemplate>
<ScrollViewer CanContentScroll="True" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Disabled">
<TextBlock Text="{Binding}" />
</ScrollViewer>
</DataTemplate>
</local:SlidingComboBox.ItemTemplate>
</local:SlidingComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
You can also use SlideForever property to manipulate RepeatBehaviour.
<local:SlidingComboBox ItemsSource="{Binding Value}" SlideForever="True" />
I've been looking into this and I think I have your solution. You should combine both a RenderTransform and a Storyboard on the ComboBox ContentPresenter (this is what displays the currently selected item)
<ComboBox Grid.Row="1" Name="MyComboBox" Width="200">
<ComboBox.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform X="0" Y="0" />
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<EventTrigger.Actions>
<BeginStoryboard x:Name="ScrollItem">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation Duration="00:00:5" From="0" To="200" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" />
<DoubleAnimation Duration="00:00:5" BeginTime="00:00:5" From="-200" To="0" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</ComboBox.Resources>
<ComboBoxItem>
I am combobox value 1
</ComboBoxItem>
<ComboBoxItem>
I am combobox value 2, Hello!
</ComboBoxItem>
</ComboBox>
Having the ComboBox of size 200, scrolling from 0 to 200, then -200 to 0, scrolls the text off the right hand side of the control, and in the left hand side. (You can drop the 2nd DoubleAnimation if you like and set AutoReverse to True to cause the text to bounce back in if you'd rather that. This does not code you around items that are too big for the control, you will need to write some code for the ComboBox so it decides if the currently selected Item is too big, and from code behind (or maybe a custom ComboBox Class) dynamically turn on/off the storyboard.
First iterate through all items of your combobox, check for the width of every items by assigning the text to a label. Then, check width every time, if width of current item gets greater than previous items then change the maximum width.
int DropDownWidth(ComboBox myCombo)
{
int maxWidth = 0;
int temp = 0;
Label label1 = new Label();
foreach (var obj in myCombo.Items)
{
label1.Text = obj.ToString();
temp = label1.PreferredWidth;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
label1.Dispose();
return maxWidth;
}
private void window_loaded(object sender, EventArgs e)
{
comboBox1.DropDownWidth = DropDownWidth(comboBox1);
}
OR
int DropDownWidth(ComboBox myCombo)
{
int maxWidth = 0, temp = 0;
foreach (var obj in myCombo.Items)
{
temp = TextRenderer.MeasureText(obj.ToString(), myCombo.Font).Width;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
return maxWidth;
}
Thanks to those guys got it from here --> Auto-width of ComboBox's content
I have a situation as follows
I want to hide delete button when no item is selected & show when something is selected.
Also hide that button if user selects an item & clicks elsewhere (which represents that user is no longer working with the listbox).
I tried LostFocus events, checking SelectedIndex etc. but no success. Any idea how to do this?
private void ListBoxItem_LostFocus(object sender, RoutedEventArgs e)
{
if (button.IsFocused != true) // checking if user has selected an item & clicking on button (valid action)
{
listbox.SelectedIndex = -1;
}
}
private void listbox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (listbox.SelectedIndex == -1)
{
button.Visibility = Visibility.Hidden;
}
else
{
button.Visibility = Visibility.Visible;
}
}
Joseph's answer is correct but you don't need a converter.
You can achieve the same result with a simple trigger:
<ListBox>
...
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<Trigger Property="SelectedItem" Value="{x:Null}">
<Setter TargetName="YOUR_BUTTON'S_NAME" Property="Visibility" Value="Hidden"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
Edit: It's indeed impossible to use TargetName in a Style Setter.
So, the closest solution I could make work is to create a DataTrigger:
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Top">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyList, Path=SelectedItem}"
Value="{x:Null}">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<TextBlock Text="Button"/>
</Button>
<ListBox x:Name="MyList">
<TextBlock Text="Something"/>
<TextBlock Text="Something"/>
<TextBlock Text="Something"/>
<TextBlock Text="Something"/>
<TextBlock Text="Something"/>
</ListBox>
</DockPanel>
For the first half of the question the best way is to use a converter like so :
1. add a Converter class :
public class VisibilityConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value == null ? Visibility.Hidden : Visibility.Visible);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
2.add it to the Window resources:
<Window.Resources>
<wpfApplication:VisibilityConverter x:Key="VisibilityConverter"/>
</Window.Resources>
3. and use it to hide/show your button :
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox HorizontalAlignment="Center" x:Name="listBox">
<ListBoxItem Content="Fist"/>
<ListBoxItem Content="Second"/>
<ListBoxItem Content="Third"/>
<ListBoxItem Content="Fourth"/>
</ListBox>
<Button Content="delete" Grid.Row="1" HorizontalAlignment="Center" Visibility="{Binding SelectedItem,ElementName=listBox,Converter={StaticResource VisibilityConverter}}"/>
</Grid>
And for the second half of the question, you must handle the MouseDown event in your window:
MouseDown="MainWindow_OnMouseDown"
and add the following handler (that will reset the SelectedItem of the list to null)
private void MainWindow_OnMouseDown(object sender, MouseButtonEventArgs e)
{
listBox.SelectedItem = null;
}
Run this code separately and try.it is working for me here.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel>
<ListBox x:Name="lst">
<ListBox.Triggers>
<EventTrigger RoutedEvent="GotFocus">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="DeleteButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0.1" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="LostFocus">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="DeleteButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0.1" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ListBox.Triggers>
<ListBoxItem>ListBoxItem1</ListBoxItem>
<ListBoxItem>ListBoxItem2</ListBoxItem>
<ListBoxItem>ListBoxItem3</ListBoxItem>
<ListBoxItem>ListBoxItem4</ListBoxItem>
</ListBox>
<Button x:Name="DeleteButton" Content="Delete" Height="30" Width="100">
<Button.Style>
<Style BasedOn="{StaticResource alredayDefinedStyleKey}" TargetType="Button">
<Setter Property="Visibility" Value="Visible"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedItem, ElementName=lst}" Value="{x:Null}" >
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
<ListBox Grid.Column="1">
<ListBoxItem>1</ListBoxItem>
<ListBoxItem>2</ListBoxItem>
</ListBox>
</Grid>
LostFocus will only fire, if the element loses Logical focus. Use LostKeyboardFocus event. Then you're one step closer.
I have a datagrid which contains hyperlinks in a DatagridTemplateColumn like the following :
<DataGridTemplateColumn Width="170" SortMemberPath="Joueur.EtatCivil.Joueur_nom" CanUserReorder="False" CanUserResize="True" Header="">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate />
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource DatagridCellHyperlinkStyle}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Padding="{TemplateBinding Padding}" Width="Auto" VerticalAlignment="Center" SnapsToDevicePixels="True">
<TextBlock Foreground="{TemplateBinding Foreground}" Width="Auto" TextTrimming="CharacterEllipsis">
<Hyperlink IsEnabled="{TemplateBinding IsEnabled}">
<InlineUIContainer TextDecorations="{Binding Path=TextDecorations, RelativeSource={RelativeSource AncestorType=TextBlock}}" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=TextBlock}}">
<ContentPresenter Content="{Binding DataContext.Joueur.EtatCivil.NomComplet, RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
</InlineUIContainer>
<Hyperlink.Style>
<Style TargetType="Hyperlink" BasedOn="{StaticResource HyperlinkStyle}">
<EventSetter Event="Hyperlink.Click" Handler="ClickEvent" />
<EventSetter Event="Hyperlink.MouseEnter" Handler="MouseOverEvent" />
<EventSetter Event="Hyperlink.MouseLeave" Handler="ClicJoueurMouseLeaveEvent" />
</Style>
</Hyperlink.Style>
</Hyperlink>
</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
This works fine and as expected but I have trouble when I just move my cursor hover many lines in a small time (<0.5s maybe).
My MouseEnterEvent calls a method named "Show();" and my MouseLeaveEvent a method named "Hide();"
This show/hide a small popup to display to the user more data. The problem is that my show(); method update a Wpf toolkit chart which seems to have trouble to be updated with this frequency (10 times in 0.5s). The binding seems to "crash" and never works again.
this helped me : Binding update adds news series to WPF Toolkit chart (instead of replacing/updating series) )
I update my chart like this :
this.colonnes.ItemsSource = null;
_DataGraph = new ObservableCollection<GraphiqueValeurs>();
_DataGraph.Add(new GraphiqueValeurs(value, "my string");
this.colonnes.ItemsSource = _DataGraph;
this.colonnes.UpdateLayout();
With the following xaml :
<chartingToolkit:Chart VerticalAlignment="Top" HorizontalAlignment="Center" FontFamily="/BPM2015;component/#Open Sans Condensed" BorderBrush="Transparent" Name="columnChart" Title="Column Series Demo" Foreground="{DynamicResource CouleurTexte}" >
<chartingToolkit:ColumnSeries Name="colonnes" Foreground="{DynamicResource CouleurTexte}" DependentValueBinding="{Binding Valeur}"
IndependentValueBinding="{Binding Name}"
DataPointStyle="{StaticResource ColorByPreferenceColumn}"
>
<chartingToolkit:ColumnSeries.DependentRangeAxis>
<chartingToolkit:LinearAxis Orientation="Y" Minimum="0" Maximum="20" Title="" Foreground="Transparent" ShowGridLines="True">
<chartingToolkit:LinearAxis.MajorTickMarkStyle>
<Style TargetType="Line">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</chartingToolkit:LinearAxis.MajorTickMarkStyle>
</chartingToolkit:LinearAxis>
</chartingToolkit:ColumnSeries.DependentRangeAxis>
</chartingToolkit:ColumnSeries>
<chartingToolkit:Chart.LegendStyle>
<Style TargetType="Control">
<Setter Property="Width" Value="0" />
<Setter Property="Height" Value="0" />
</Style>
</chartingToolkit:Chart.LegendStyle>
<chartingToolkit:Chart.TitleStyle>
<Style TargetType="Control">
<Setter Property="Width" Value="0" />
<Setter Property="Height" Value="0" />
</Style>
</chartingToolkit:Chart.TitleStyle>
So my question is : How I can avoid all these quick mouseEnter ? I'd like to avoid my mouseEnter function to call the "Show()" method if the cursor doesn't stay at least 0.4s on the hyperlink.
How could I do that ?
This
Thank you
You can use a DispatcherTimer to add a delay to calling your function and combine that with a bool flag that is set to true in the MouseEnter handler and set to false in the MouseLeave handler. Try this:
private DispatcherTimer timer = new DispatcherTimer();
private isMouseOver = false;
...
timer.Interval = TimeSpan.FromMilliseconds(400);
timer.Tick += Timer_Tick;
...
private void MouseEnterHandler(object sender, MouseEventArgs e)
{
isMouseOver = true;
timer.Start();
}
private void MouseLeaveHandler(object sender, MouseEventArgs e)
{
isMouseOver = false;
timer.Stop();
}
private void Timer_Tick(object sender, EventArgs e)
{
if (isMouseOver) Show();
timer.Stop();
}
I need a thumb control that can be sized using a mouse. When the user hovers the mouse over one of the ends a size cursor should be displayed and when the user clicks and drags the end of the control it will be re-sized.
How can that be achieved?
Here is one I made a while ago, it allows Move and Resize, but you can remove the Move logic and it should work fine (the style is still a bit messy, but it works pretty well)
Its based on ContentControl so you can add any Element inside and Move/Resize on a Canvas, It uses 3 Adorners, one for Resize, one for Move and one to display information (current size)
Here is a full working example if you want to test/use/modify/improve :)
Code:
namespace WpfApplication21
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class ResizeThumb : Thumb
{
public ResizeThumb()
{
DragDelta += new DragDeltaEventHandler(this.ResizeThumb_DragDelta);
}
private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Control designerItem = this.DataContext as Control;
if (designerItem != null)
{
double deltaVertical, deltaHorizontal;
switch (VerticalAlignment)
{
case VerticalAlignment.Bottom:
deltaVertical = Math.Min(-e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
designerItem.Height -= deltaVertical;
break;
case VerticalAlignment.Top:
deltaVertical = Math.Min(e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + deltaVertical);
designerItem.Height -= deltaVertical;
break;
default:
break;
}
switch (HorizontalAlignment)
{
case HorizontalAlignment.Left:
deltaHorizontal = Math.Min(e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + deltaHorizontal);
designerItem.Width -= deltaHorizontal;
break;
case HorizontalAlignment.Right:
deltaHorizontal = Math.Min(-e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
designerItem.Width -= deltaHorizontal;
break;
default:
break;
}
}
e.Handled = true;
}
}
public class MoveThumb : Thumb
{
public MoveThumb()
{
DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta);
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Control designerItem = this.DataContext as Control;
if (designerItem != null)
{
double left = Canvas.GetLeft(designerItem);
double top = Canvas.GetTop(designerItem);
Canvas.SetLeft(designerItem, left + e.HorizontalChange);
Canvas.SetTop(designerItem, top + e.VerticalChange);
}
}
}
public class SizeAdorner : Adorner
{
private Control chrome;
private VisualCollection visuals;
private ContentControl designerItem;
protected override int VisualChildrenCount
{
get
{
return this.visuals.Count;
}
}
public SizeAdorner(ContentControl designerItem)
: base(designerItem)
{
this.SnapsToDevicePixels = true;
this.designerItem = designerItem;
this.chrome = new Control();
this.chrome.DataContext = designerItem;
this.visuals = new VisualCollection(this);
this.visuals.Add(this.chrome);
}
protected override Visual GetVisualChild(int index)
{
return this.visuals[index];
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
this.chrome.Arrange(new Rect(new Point(0.0, 0.0), arrangeBounds));
return arrangeBounds;
}
}
}
Xaml:
<Window x:Class="WpfApplication21.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication21"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<local:MoveThumb Cursor="SizeAll">
<local:MoveThumb.Style>
<Style TargetType="{x:Type local:MoveThumb}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MoveThumb}">
<Rectangle Fill="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:MoveThumb.Style>
</local:MoveThumb>
<Control x:Name="resizer">
<Control.Style>
<Style TargetType="{x:Type Control}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control}">
<Grid>
<Grid Opacity="0" Margin="-3">
<local:ResizeThumb Height="3" Cursor="SizeNS" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<local:ResizeThumb Width="3" Cursor="SizeWE" VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
<local:ResizeThumb Width="3" Cursor="SizeWE" VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
<local:ResizeThumb Height="3" Cursor="SizeNS" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
<local:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNWSE" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<local:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNESW" VerticalAlignment="Top" HorizontalAlignment="Right"/>
<local:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNESW" VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<local:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNWSE" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
</Grid>
<Grid IsHitTestVisible="False" Opacity="1" Margin="-3">
<Grid.Resources>
<Style TargetType="{x:Type Ellipse}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Stroke" Value="#FFC8C8C8" />
<Setter Property="StrokeThickness" Value=".5" />
<Setter Property="Width" Value="7" />
<Setter Property="Height" Value="7" />
<Setter Property="Margin" Value="-2" />
<Setter Property="Fill" Value="Silver" />
</Style>
</Grid.Resources>
<Rectangle SnapsToDevicePixels="True" StrokeThickness="1" Margin="1" Stroke="Black" StrokeDashArray="4 4"/>
<Ellipse HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Ellipse HorizontalAlignment="Right" VerticalAlignment="Top"/>
<Ellipse HorizontalAlignment="Left" VerticalAlignment="Bottom"/>
<Ellipse HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Control.Style>
</Control>
<Grid x:Name="sizeInfo" SnapsToDevicePixels="True">
<Path Stroke="Red" StrokeThickness="1" Height="10" VerticalAlignment="Bottom" Margin="-2,0,-2,-15" Stretch="Fill" Data="M0,0 0,10 M 0,5 100,5 M 100,0 100,10"/>
<TextBlock Text="{Binding Width}" Background="White" Padding="3,0,3,0" Foreground="Red" Margin="0,0,0,-18" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
<Path Stroke="Red" StrokeThickness="1" Width="10" HorizontalAlignment="Right" Margin="0,-2,-15,-2" Stretch="Fill" Data="M5,0 5,100 M 0,0 10,0 M 0,100 10,100"/>
<TextBlock Text="{Binding Height}" Background="White" Foreground="Red" Padding="3,0,3,0" Margin="0,0,-18,0" HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="90" CenterX="1" CenterY="0.5"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
<ContentPresenter Content="{TemplateBinding Content}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="sizeInfo" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="sizeInfo" Property="Visibility" Value="Hidden" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Canvas>
<ContentControl Width="183" Height="110" Canvas.Left="166" Canvas.Top="50" />
</Canvas>
</Window>
Result:
With content inside (Button)
Sorry the cursors do not show when using SnipTool