how to select item on ExpanderDoubleClick Header in Listview - c#

I have a ListView with different itemTemplate, and each itemTemplates have an ExpanderDoubleClick : Expander inside
I would like to select items in extended Mode in a listview with custom selection design (declared in each items UserControl).
So here is my xaml for the listView:
<ListView x:Name="ListViewModules" ItemsSource="{Binding ListOfModules}"
ItemContainerStyle="{StaticResource ContainerListViewItemStyle}"
ItemTemplateSelector="{DynamicResource ModuleTemplateSelector}"
SelectionMode="Extended"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
Grid.Column="0" Grid.Row="0" Height="494" Width="634" Background="#FFCDCDCD"
ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Visible"
BorderBrush="#666666" BorderThickness="1" Padding="0"
ClipToBounds="True" SnapsToDevicePixels="True" >
</ListView>
with styles for removing the standard blue selection (declared in App.xaml):
<Style x:Key="ContainerListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Margin" Value="2,2,2,0"/>
<Setter Property="dd:DragDrop.DragSourceIgnore" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border x:Name="Bd" BorderBrush="Transparent" BorderThickness="0" Background="Transparent" Padding="0" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have different ItemTemplates with each "UserControl's" and here an exemple of one of them with it's selection design :
<UserControl x:Class="Topinambour.Templates.Modules.DelayTemplate" ...>
<Grid Width="602" MinHeight="24">
<Grid.ColumnDefinitions> <ColumnDefinition Width="40"/><ColumnDefinition /> </Grid.ColumnDefinitions>
<ToggleButton x:Name="TgBtIsActive" IsChecked="{Binding IsChecked}" IsThreeState="{Binding IsThreeState}" Grid.Column="0" Height="24" Width="40" click="TgBtIsActive_Click"/>
<templates:ExpanderDoubleClick x:Name="ModuleExpander" IsExpanded="{Binding IsExpanded, Mode=OneWay}" Height="auto" Template="{DynamicResource TemplateExpander}" Grid.Column="1" Margin="4,0,0,0" Collapsed="Expander_Collapsed" Expanded="Expander_Expanded" HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
<templates:ExpanderDoubleClick.Resources>
<Style x:Key="ExpanderHeaderStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="BorderIndent" BorderBrush="Gray" BorderThickness="1" Height="24" Width="558" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Padding="{Binding Indent}">
... my Header Content ...
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsExpanded}" Value="true">
<Setter Property="IsEnabled" TargetName="TbComment" Value="true"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListViewItem}}" Value="true">
<Setter Property="Background" TargetName="headerCanvas" Value="#999999"/>
<Setter Property="BorderBrush" TargetName="TbComment" Value="#666666"/>
<Setter Property="Foreground" TargetName="LbHeaderTitle" Value="White"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</templates:ExpanderDoubleClick.Resources>
<Border Background="White" BorderBrush="#666666" BorderThickness="1,0,1,1" Height="136" Width="558">
<Grid>
... my Expanded Content ...
</Grid>
</Border>
</templates:ExpanderDoubleClick>
</Grid>
and here the ExpanderDoubleClick.cs :
public class ExpanderDoubleClick: Expander
{
private static readonly DependencyPropertyKey IsMouseDoubleClickedPropertyKey = DependencyProperty.RegisterReadOnly(
"IsMouseDoubleClicked",typeof(Boolean),typeof(ExpanderDoubleClick),new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsMouseDoubleClickedProperty = IsMouseDoubleClickedPropertyKey.DependencyProperty;
public Boolean IsMouseDoubleClicked
{
get { return (Boolean)GetValue(IsMouseDoubleClickedProperty); }
}
static ExpanderDoubleClick()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExpanderDoubleClick), new FrameworkPropertyMetadata(typeof(ExpanderDoubleClick)));
}
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnMouseDoubleClick(e);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ContentControl contentControl = base.GetTemplateChild("HeaderSite") as ContentControl;
if (contentControl != null)
{
contentControl.AddHandler(ContentControl.MouseDoubleClickEvent, new MouseButtonEventHandler(ExpanderHeader_MouseDoubleClick), true);
}
}
private void ExpanderHeader_MouseDoubleClick(Object sender, MouseButtonEventArgs e)
{
base.SetValue(IsMouseDoubleClickedPropertyKey, !IsMouseDoubleClicked);
base.IsExpanded= !base.IsExpanded;
}
}
the selection is not working when I click on the header of the expander but if I open it and click inside the expandedContent, it select the item ! what did I miss ? thank you for the reply.

I finally found a pretty good solution that keep my extended mode selection.
since the header seams to not receive Click event to handle.
I looked up the tree debug, and it shows that the click on header does not go through the "ListViewItem"
so I add an OnMouseLeftButtonDown Event on my border x.Name="BorderIdent" and then add RaisedEvent back to it's itemTemplate
like so :
private void BorderIndent_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ListViewItem lvItem = UITools.FindAncestor<ListViewItem>(e.OriginalSource as DependencyObject);
bool isListViewItem = lvItem != null;
if (isListViewItem)
{
lvItem.RaiseEvent(e);
}
}
UITools.FindAncestor : is here
and all worked perfectly :)

Related

Inherited WPF custom control does not inherit parent Command

I've created a IconButton for use in WPF/XMAML. It should be able to display an Icon MDL2 Assets font on top and an text on bottom. It should have the appearance of an default WPF toolbar button. I decided to create a custom control which inherits from default WPF button.
So I created the custom control and added Dependency Properties for Text and the somehow cryptic MDL2IconCode:
public class IconButton : Button
{
public static readonly DependencyProperty TextProperty;
public static readonly DependencyProperty MDL2IconCodeProperty;
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string MDL2IconCode
{
get { return (string)GetValue(MDL2IconCodeProperty); }
set { SetValue(MDL2IconCodeProperty, value); }
}
static IconButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(IconButton),
new FrameworkPropertyMetadata(typeof(IconButton)));
TextProperty = DependencyProperty.Register("Text",
typeof(string),
typeof(IconButton),
new PropertyMetadata("Button text", OnTextChanged));
MDL2IconCodeProperty = DependencyProperty.Register("MDL2IconCode",
typeof(string),
typeof(IconButton),
new PropertyMetadata("\uf13e", OnIconTextChanged));
}
static void OnTextChanged(DependencyObject o,
DependencyPropertyChangedEventArgs e)
{
var iconButton = o as IconButton;
if (iconButton == null)
{
return;
}
string newText = e.NewValue as string;
iconButton.Text = newText;
}
static void OnIconTextChanged(DependencyObject o,
DependencyPropertyChangedEventArgs e)
{
var iconButton = o as IconButton;
if (iconButton == null)
{
return;
}
string newText = e.NewValue as string;
iconButton.MDL2IconCode = newText;
}
}
The ResourceDictionary of Generic.xaml looks like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UI.CustomControls">
<Style TargetType="{x:Type local:IconButton}"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:IconButton}">
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
<StackPanel>
<TextBlock HorizontalAlignment="Center"
Text="{TemplateBinding MDL2IconCode}"
FontFamily="Segoe MDL2 Assets"
FontSize="16"
x:Name="iconTextBlock"/>
<TextBlock HorizontalAlignment="Center"
Text="{TemplateBinding Text}"
x:Name="textTextBlock"/>
</StackPanel>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
The button looks as it should.
But the command binding in XAML isn't working anymore. But it should work, as per inheritance it still is a button..
Maybe anyone has an idea what to add to make the command binding work?
Bind the command of the Button inside of your control template to the templated parent.
<ControlTemplate TargetType="{x:Type local:IconButton}">
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"
CommandTarget="{TemplateBinding CommandTarget}">
<!-- ...other code. -->
</Button>
</ControlTemplate>
But it should work, as per inheritance it still is a button..
No. The Button inside of your control template does not magically bind to the corresponding properties of its templated parent, regardless if it is derived from Button or any other control. You will have to do so for other dependency properties like the CommandParameter as well.
Please also note that TemplateBinding is an optimized binding that does not have all capabilities of the more powerful Binding markup extension. Consequently, when TemplateBinding does not work, e.g. in two-way binding scenarios, you can use TemplatedParent like this:
{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MyDependencyProperty}
For more information, you can refer to the TemplateBinding documentation.
The ControlTemplate of a Button shouldn't include another Button.
You should template your control to look like a Button if that's what you want:
<Style TargetType="{x:Type local:IconButton}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:IconButton}">
<Border Name="Bd" Background="{TemplateBinding Control.Background}"
BorderBrush="{TemplateBinding Control.BorderBrush}"
BorderThickness="{TemplateBinding Control.BorderThickness}"
Padding="{TemplateBinding Control.Padding}" SnapsToDevicePixels="true">
<ContentControl HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}">
<StackPanel>
<TextBlock HorizontalAlignment="Center"
Text="{TemplateBinding MDL2IconCode}"
FontFamily="Segoe MDL2 Assets"
FontSize="16"
x:Name="iconTextBlock"/>
<TextBlock HorizontalAlignment="Center"
Text="{TemplateBinding Text}"
x:Name="textTextBlock"/>
</StackPanel>
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="true">
<Setter TargetName="Bd" Value="#80DADADA" Property="BorderBrush"/>
<Setter TargetName="Bd" Value="#FFB6BDC5" Property="Background"/>
</Trigger>
<Trigger Property="UIElement.IsKeyboardFocused" Value="true">
<Setter TargetName="Bd" Value="#80DADADA" Property="BorderBrush"/>
<Setter TargetName="Bd" Value="#FFB6BDC5" Property="Background"/>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="true">
<Setter TargetName="Bd" Value="#90006CD9" Property="BorderBrush"/>
<Setter TargetName="Bd" Value="#400080FF" Property="Background"/>
</Trigger>
<Trigger Property="UIElement.IsEnabled" Value="false">
<Setter Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" Property="Foreground"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It will then behave like any other Button which means that you can bind its Command property as usual:
<local:IconButton Command="{Binding YourCommand}" />

How to disable all toggle buttons except one that clicked?

I'm making an application using C# and WPF
I have 8 toggle buttons.
When I click one of the buttons others should be disabled so I can't click it except one is activated.
When I click this button again others should be enabled
Styles.xaml:
<!--Toggle Button-->
<Style x:Key="ToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#535353"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="MinHeight" Value="30"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="{TemplateBinding Background}"
BorderThickness="0">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml:
<ToggleButton x:Name="CategoryToggle1"
Grid.Row="1"
Style="{StaticResource ToggleButton}"
Checked="ShowOptions"
Unchecked="HideOptions" />
How Can I achieve this by code and XAML?
I want to do it like in this video:
Video
Thank you for comments guys, I found another solution.
MainWindow.XAML:
<RadioButton x:Name="CategoryToggle1"
Grid.Row="1"
Grid.Column="1"
Style="{StaticResource RadioButton}"
GroupName="ToggleButtonsGroup"
Checked="OpenOptions"
Unchecked="HideOptions"/>
Styles.xaml:
<!--Radio Button-->
<Style TargetType="RadioButton"
x:Key="RadioButton"
BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--Toggle Button-->
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#535353" />
<Setter Property="MinHeight" Value="30" />
<Setter Property="Grid.Column" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Background="{TemplateBinding Background}"
BorderThickness="0">
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
<Trigger Property="IsEnabled" Value="true">
<Setter Property="Opacity" Value="1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.cs:
private void OpenOptions(object sender, RoutedEventArgs e){
RadioButton radioButton = sender as RadioButton;
radioButton.IsChecked = true;
//Disable all option buttons except one that active
MyGrid.Children.OfType<RadioButton>().Where(rb => rb != radioButton &&
rb.GroupName == radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = false);
}
private void HideOptions(object sender, RoutedEventArgs e)
{
RadioButton radioButton = sender as RadioButton;
MyGrid.Children.OfType<RadioButton>().Where(rb => rb.GroupName ==
radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = true);
}
Using Click events of each ToggleButton
One way you could do it is by giving a name to all your ToggleButtons, hook-up to their Click event and manually uncheck others in the code-behind:
XAML
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="MaxWidth" Value="15"/>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button1" Content="Button 1" Click="button1_Click"/>
<TextBlock Text="Line 1"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button2" Content="Button 2" Click="button2_Click"/>
<TextBlock Text="Line 2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button3" Content="Button 3" Click="button3_Click"/>
<TextBlock Text="Line 3"/>
</StackPanel>
</StackPanel>
Code-behind
private void button1_Click(object sender, RoutedEventArgs e) {
if (button1.IsChecked == true) {
button2.IsChecked = false;
button3.IsChecked = false;
}
}
private void button2_Click(object sender, RoutedEventArgs e) {
if (button2.IsChecked == true) {
button1.IsChecked = false;
button3.IsChecked = false;
}
}
private void button3_Click(object sender, RoutedEventArgs e) {
if (button3.IsChecked == true) {
button1.IsChecked = false;
button2.IsChecked = false;
}
}
This method is tedious, error-prone, requires code-behind and is not very scalable.
Binding IsChecked properties to a collection of bool with one true at a time.
Another way you could go (still by using code-behind) is to define a collection of boolean values and bind each ToggleButton.IsChecked on one of the bool in the collection, and ensure that the collection only contains at most one true at a time:
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button1" Content="Button 1" IsChecked="{Binding [0]}"/>
<TextBlock Text="Line 1"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button2" Content="Button 2" IsChecked="{Binding [1]}"/>
<TextBlock Text="Line 2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button3" Content="Button 3" IsChecked="{Binding [2]}"/>
<TextBlock Text="Line 3"/>
</StackPanel>
Code-behind
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
ObservableCollection<bool> states = new ObservableCollection<bool> { false, false, false };
states.CollectionChanged += States_CollectionChanged;
DataContext = states;
}
private void States_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
var collection = sender as ObservableCollection<bool>;
if (e.Action == NotifyCollectionChangedAction.Replace) {
if ((bool)e.NewItems[0]) {
for (int i = 0; i < collection.Count; i++) {
if (e.NewStartingIndex != i) {
collection[i] = false;
}
}
}
}
}
}
Again, this uses code-behind and not the view model but at least it is easier to add rows.
The behavior you need is very specific.
I don't know how, but i got here trying to make a toggle button behave like a radio button. Your answer was enlightning.
For what it's worth, here's how you would do that :
Resource :
<Style x:Key='RadioToggle' TargetType='RadioButton'
BasedOn='{StaticResource {x:Type ToggleButton}}' />
Control :
<RadioButton Content='RadioToggle1' IsChecked='True'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle2'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle3'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />

How to save position after reload DataGrid in WPF

I'm talking about DataGrid not DataGridView!
What I'm doing here is refreshing datagrid which contains <DataGrid.GroupStyle> and I'm showing my data from database grouped by number of order, and what I want to do is refresh my datagrid.
Here is my code:
public MainWindow()
{
try
{
InitializeComponent();
this.WindowStartupLocation = WindowStartupLocation.CenterScreen;
this.WindowState = WindowState.Maximized;
var ordersList = OrdersController.localOrders();
collectionViewSource.Source = ordersList;
collectionViewSource.GroupDescriptions.Add(new PropertyGroupDescription("NumberOfOrder"));
DataContext = collectionViewSource;
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(8);
timer.Tick += timer_Tick;
timer.Start();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void timer_Tick(object sender, EventArgs e)
{
var ordersList = OrdersController.localOrders();
collectionViewSource.Source = null;
collectionViewSource.Source = ordersList;
DataContext = collectionViewSource;
datagrid1.ScrollIntoView(datagrid1.Items[datagrid1.Items.Count - 1]);
}
}
XAML:
<DataGrid HorizontalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Hidden" BorderBrush="#83D744" IsSynchronizedWithCurrentItem="False" VerticalGridLinesBrush="Transparent" Grid.Column="0" RowHeaderWidth="0" CanUserAddRows="False" AutoGenerateColumns="False" x:Name="datagrid1" Margin="10,150,8,50" Background="Transparent" RowBackground="#FF494949" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" ItemsSource="{Binding}">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="#83D744"/>
<Setter Property="Opacity" Value="1"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="18"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="Height" Value="50"/>
</Style>
<Style x:Key="TextInCellCenter" TargetType="{x:Type TextBlock}" >
<Setter Property="TextAlignment" Value="Center"/>
</Style>
<Style TargetType="{x:Type TextBlock}" x:Key="RightAligElementStyle">
<Setter Property="TextAlignment" Value="Right"/>
</Style>
<Style TargetType="{x:Type TextBlock}" x:Key="LeftAligElementStyle">
<Setter Property="TextAlignment" Value="Left"/>
</Style>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent"/>
</DataGrid.Resources>
<DataGrid.Columns >
<DataGridTextColumn Binding="{Binding ProductName}" ElementStyle="{StaticResource LeftAligElementStyle}" Header="NAZIV ARTIKLA" MinWidth="350" Foreground="White" FontSize="20" FontFamily="Verdana" />
<DataGridTextColumn Binding="{Binding Quantity}" ElementStyle="{StaticResource TextInCellCenter}" Header="KOLIČINA" MinWidth="200" Foreground="White" FontSize="20" FontFamily="Verdana" />
</DataGrid.Columns>
<DataGrid.GroupStyle>
<!-- Style for groups at top level. -->
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Background="Black" Opacity="0.7">
<Expander.Header >
<DockPanel Height="50" Margin="0,0,0,0" Name="dockPanel" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}, Path=ActualWidth}">
<Button Name="btnFinishOrder" Content="Finish Order" Margin="0,0,55,5" DockPanel.Dock="Right" Click="btnFinishOrder_Click" FontSize="12" BorderThickness="1.5" HorizontalAlignment="Left" VerticalAlignment="Bottom" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Foreground="#83D744" Background="Transparent" BorderBrush="#83D744" Width="130" Height="40">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush= "{TemplateBinding BorderBrush}"
Background= "{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
<Button Name="btnTakeIt" Click="btnTakeIt_Click" Content="Take it" Margin="0,0,20,5" DockPanel.Dock="Right" FontSize="12" BorderThickness="1.5" HorizontalAlignment="Left" VerticalAlignment="Bottom" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Foreground="#83D744" Background="Transparent" BorderBrush="#83D744" Width="130" Height="40">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
<TextBlock FontWeight="Normal" FontFamily="Verdana" FontSize="20" Height="25" Foreground="#83D744" Text="{Binding Path=Name,StringFormat= Number of Order:# {0}}" />
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
after reload data, cell selection jumps on first column and scrollbars is reset. How to save position of DataGrid?
If you need to scroll to specific item, just save the index and use:
dataGrid.ScrollIntoView(dataGrid.Items[itemIndex]);
You can use this to scroll to the last item in the DataGrid:
dataGrid.ScrollIntoView(dataGrid.Items[dataGrid.Items.Count - 1]);
EDIT:
There seems to be a problem with DataGrid scrolling when using CollectionViewSource and grouping.
I managed to get it to work the other way... Instead of
dataGrid.ScrollIntoView(dataGrid.Items[dataGrid.Items.Count - 1])
try using the following code to do the scrolling:
if (datagrid1.Items.Count > 0)
{
var border = VisualTreeHelper.GetChild(datagrid1, 0) as Decorator;
if (border != null)
{
var scrollViewer = border.Child as ScrollViewer;
if (scrollViewer != null) scrollViewer.ScrollToEnd();
}
}

How to access controls in inside Control Template in a Custom control

I am developing a WPF Custom Control
<ResourceDictionary xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MultiSelectComboBox">
<Style TargetType="{x:Type local:MultiSelectComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MultiSelectComboBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions >
<telerik:RadComboBox x:Name="PART_ComboBox"
Grid.Column="0"
ItemsSource="{Binding ItemsSource,RelativeSource={RelativeSource TemplatedParent}}" >
<telerik:RadComboBox.Template>
<ControlTemplate>
<TextBlock x:Name="PART_ComboText"/>
</ControlTemplate>
</telerik:RadComboBox.Template>
<telerik:RadComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="PART_ItemCheckBox"/>
<TextBlock x:Name="PART_ItemText"/>
</StackPanel>
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
<CheckBox Grid.Column="1" x:Name="PART_SelectAllCheckBox" VerticalAlignment="Center" IsChecked="{TemplateBinding IsAllSelected}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
I want to get access to PART_ComboText
I can access controls define there using GetTempalteChild method
part_comboBox = GetTemplateChild("PART_ComboBox") as RadComboBox;
But I am unable to access controls inside Control Template. For an example
which is in Control template couldn't access that. I know we couldn't access control templates from code behind.
I tried this method. It also does not work.
part_comboBox = GetTemplateChild("PART_ComboBox") as RadComboBox;
var comboBoxTemplate = part_comboBox.Template;
part_comboText = (TextBlock) comboBoxTemplate.FindName("PART_ComboText", part_comboBox);
This is probably because the RadComboBox isn't fully loaded when your trying to find the TextBlock inside.
Check its IsLoaded property to know if it's ready or not. If it's not, you'll have to defer the execution of your code until its Loaded event is raised.
part_comboBox = GetTemplateChild("PART_ComboBox") as RadComboBox;
if (part_comboBox.IsLoaded)
{
part_comboText = part_comboBox.FindName("PART_ComboText");
DoStuffWithComboText(part_comboText);
}
else
{
part_comboBox.Loaded = new RoutedEventHandler((o, e) =>
{
part_comboText = part_comboBox.FindName("PART_ComboText");
// Or... part_comboText = part_comboBox.Template.FindName("PART_ComboText", part_comboBox); ... can't remember which one was correct in this case
DoStuffWithComboText(part_comboText);
}
}
Use OnApplyTemplate() to manipulate child controls and call this inside the static method PropertyChangedCallback:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (Template != null)
{
Image partImage = Template.FindName("PART_Image", this) as Image;
if (partImage != null)
{
if (String.IsNullOrEmpty(Picture))
{
partImage.Visibility = Visibility.Hidden;
partImage.Width = 0;
}
else
{
partImage.Visibility = Visibility.Visible;
partImage.Width = 16;
}
}
}
}
public string Picture
{
get => (string)GetValue(PictureProperty);
set => SetValue(PictureProperty, value);
}
private static void OnPictureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ButtonUI control = d as ButtonUI;
control.OnApplyTemplate();
}
The generic in themes is simple:
<Style TargetType="{x:Type local:ButtonUI}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Height" Value="24" />
<Setter Property="Width" Value="100" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ButtonUI}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<WrapPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Image Name="PART_Image" Source="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:ButtonUI}},Path=Picture}" Height="16" Width="16" Margin="2,0,2,0" />
<TextBlock Name="PART_Caption" Text="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:ButtonUI}},Path=Caption}" Margin="2,0,2,0" />
</WrapPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

How to alter property of a parent control, in a event of a child object?

<Style TargetType="controls:ModernVerticalMenu" >
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:ModernVerticalMenu">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{TemplateBinding ListWidth}"/>
<ColumnDefinition Width="{TemplateBinding ListWidth}"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Background="{DynamicResource background}" Height="{TemplateBinding Height}" BorderThickness="1" BorderBrush="{DynamicResource bordaSuperior}">
<!-- link list -->
<ListBox x:Name="LinkList" ItemsSource="{TemplateBinding Links}"
ScrollViewer.HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="50" Background="Transparent" Width="500">
<Border Padding="10">
<Path x:Name="icon" Data="{Binding IconData}" Stretch="Fill" Fill="{DynamicResource Accent}" Width="20" Height="20" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Border>
<TextBlock x:Name="texto" ToolTip="{Binding Tooltip}" Text="{Binding DisplayName}" Margin="45,2,2,2" FontSize="{DynamicResource MediumFontSize}" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" HorizontalAlignment="Left" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IconData}" Value="{x:Null}">
<Setter Property="Margin" TargetName="texto">
<Setter.Value>
<Thickness Bottom="2" Top="2" Left="10" Right="2"/>
</Setter.Value>
</Setter>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="icon">
<Setter.Value>
<SolidColorBrush Color="#f2f2f2" />
</Setter.Value>
</Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I created a custom control and I am creating a template for a vertical menu, I wanna that in the event of MouseOver of the ListBox, I could set the value of proprierty of the controls:ModernVerticalMenu,
any idea?
When I write parent, is the ModerVerticalMenu and child is the ListBox from the ControlTemplate.
The main idea is to somehow get access to parent object and change its property. In order to do this I create one attached property retrieving access to parent by TemplatedParent markup, second attached property stores value which is supposed to be applied in parent's property. This sample is not precise your problem but on the right track, I believe.
I want to change button Content, which is initially Empty. First textblock reflects change in Button's Content to find out whether any occured.
<StackPanel>
<TextBlock Text="{Binding ElementName=but, Path=Content}"/>
<Button Name="but" Content="" Background="CadetBlue" BorderBrush="CadetBlue" BorderThickness="2">
<Button.Template>
<ControlTemplate TargetType="Button">
<TextBox Margin="50" Text="xD" local:MyClass.Parent="{Binding RelativeSource={RelativeSource AncestorType=Button}}">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="local:MyClass.Content" Value="{x:Null}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True">
<Setter Property="Foreground" Value="CadetBlue"/>
<Setter Property="local:MyClass.Content" Value="tekst"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
Class with attached properties looks as follows
public static class MyClass
{
public static void SetParent(DependencyObject obj, Button val)
{
obj.SetValue(ParentProperty, val);
}
public static Button GetParent(DependencyObject obj)
{
return (Button)obj.GetValue(ParentProperty);
}
public static readonly DependencyProperty ParentProperty = DependencyProperty.RegisterAttached("Parent", typeof(Button), typeof(MyClass), new PropertyMetadata(null, new PropertyChangedCallback(
(x, y) =>
{
Debug.WriteLine(GetParent(x));
})));
public static void SetContent(DependencyObject obj, string val)
{
obj.SetValue(ContentProperty, val);
}
public static string GetContent(DependencyObject obj)
{
return (string)obj.GetValue(ContentProperty);
}
public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached("Content", typeof(string), typeof(MyClass), new PropertyMetadata(null, new PropertyChangedCallback(
(x, y) =>
{
if (GetContent(x) != String.Empty)
((Button)GetParent(x)).Content = GetContent(x);
})));
}
When mouse is over textbox then parent's property content changes to value of attached property set in setter
<Setter Property="local:MyClass.Content" Value="tekst"/>

Categories