I have applied a custom style to my DataGrid's column headers. Here is a simplified version of it:
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<Grid>
<Border BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Grid>
<StackPanel Orientation="Horizontal">
<ContentPresenter Margin="5 0 5 0" HorizontalAlignment="Center" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<Image IsHitTestVisible="True" Source="pin.png">
<Image.InputBindings>
<MouseBinding MouseAction="LeftClick" Command="{x:Static myView:MyCommand}" CommandParameter="{Binding}" />
</Image.InputBindings>
</Image>
</StackPanel>
</Grid>
</Border>
<Thumb x:Name="PART_LeftHeaderGripper" Style="{StaticResource DataGridColumnHeaderResizeThumb}"/>
<Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource DataGridColumnHeaderResizeThumb}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am struggling to get the command to fire when clicking the image. Is there something I am missing? Is there a different way that I can fire a command when clicking an element (i.e. an image) within the DataGridColumnHeader?
Some more details:
MyCommand is defined in the Window's CommandBindings. I have not included this code here. I have other commands defined similarly for other elements (i.e. DataGridCell) that work OK. It seems there is something specific to the way the DataGridColumnHeader element that prevents the command to be fired.
It seems like you're binding to the type MyCommand under xmlns myView; instead, you should be binding to an instance of MyCommand (assuming class MyCommand implements ICommand). You may consider implementing singleton here. Or you could define a static instance of MyCommand anywhere.
Related
Default NumericUpDown looks like that:
,
and because of buttons take a lot of place I want to make it more Windows form style, like this:
.
I've tried to play with ButtonSpinner Styles, but didn't work out, it shrinks the whole NumericUpDown, not only ButtonSpinner (even if I write another style for NumericUpDown).
<Style Selector="ButtonSpinner">
<Setter Property="Height">
<Setter.Value>
10
</Setter.Value>
</Setter>
<Setter Property="Width">
<Setter.Value>
25
</Setter.Value>
</Setter>
</Style>
What I should do to achieve desired result?
There are two solutions:
You could modify the ButtonSpinner template (or NumericUpDown /template/ ButtonSpinner template) to use the UniformGrid with 2 rows instead of StackPanel with horizontal orientation used by the Fluent theme. However you may encounter some problems with missing static resources, so you have to remember to include them in your file.
You could use the Simple theme instead of Fluent one. It uses a template with UniformGrid with 2 rows by default, but it would change the look of all controls.
So, #radoslawik proposed a good solution, but there was one problem -- he linked to nightly build of Avalonia and you may use another version. If you are you must find source code of your version (they are tagged in github) and look up styles there. For my version (0.10.18 -- it's last non-preview available at the moment) solution is next:
<Window.Styles>
<Style Selector="NumericUpDown /template/ ButtonSpinner">
<Setter Property="Template">
<ControlTemplate>
<DataValidationErrors>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
MinHeight="{TemplateBinding MinHeight}">
<DockPanel>
<UniformGrid Rows="2" Name="PART_SpinnerPanel"
DockPanel.Dock="Right"
IsVisible="{TemplateBinding ShowButtonSpinner}">
<RepeatButton Name="PART_IncreaseButton"
Classes="ButtonSpinnerRepeatButton"
VerticalContentAlignment="Center"
Foreground="{TemplateBinding Foreground}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
FontSize="{TemplateBinding FontSize}">
<Path Fill="{TemplateBinding Foreground}"
Width="16"
Height="8"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{StaticResource ButtonSpinnerIncreaseButtonIcon}" />
</RepeatButton>
<RepeatButton Name="PART_DecreaseButton"
Classes="ButtonSpinnerRepeatButton"
Foreground="{TemplateBinding Foreground}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
VerticalContentAlignment="Center"
FontSize="{TemplateBinding FontSize}">
<Path Fill="{TemplateBinding Foreground}"
Width="16"
Height="8"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{StaticResource ButtonSpinnerDecreaseButtonIcon}" />
</RepeatButton>
</UniformGrid>
<ContentPresenter Name="PART_ContentPresenter"
Grid.Column="1"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}" />
</DockPanel>
</Border>
</DataValidationErrors>
</ControlTemplate>
</Setter>
</Style>
</Window.Styles>
enter image description here
Basically, this is what it looks like, I want my tabitem to automatically make the header text fit inside the tabitem. Thanks.
WPF tabitem widths are adaptive unless you specify a width. If you don't specify a width, check if you have fixed the width of the content control or container when you define the TabItem style.
<TabControl>
<TabItem Header="long"/>
<TabItem Header="longlonglonglong"/>
</TabControl>
Initial control template
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Border x:Name="mainBorder" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1" Margin="0">
<Border x:Name="innerBorder" Background="{StaticResource TabItem.Selected.Background}" BorderBrush="{StaticResource TabItem.Selected.Border}" BorderThickness="1" Margin="-1" Opacity="0"/>
</Border>
<ContentPresenter x:Name="contentPresenter" ContentSource="Header" Focusable="False" HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
</Grid>
</ControlTemplate>
I'm using wpf datagrid, and I'm using grouping to group my orders by number of order, and I also have a status for each order item, like : is it proceed or not, but somehow it looks messy on screen if I list each status for each item, because if one item of each order is proceed that means all of items are also proceed, so I'm wondering is it possible to move status next to Order number (Expander header - DockPanel) so I might get look like this:
Order number :# 1 - Order is in progress.
Order number :# 2 - Order is in progress.
Order number :# 3 - Order is not in progress.
So question is:
IS IT POSSIBLE TO MOVE 'ORDER STATUS' NEXT TO Order Number part?:)
Here is my code:
<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= Order Number:# {0}}" />
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
code behind:
public partial class MainWindow : Window
{
CollectionViewSource collectionViewSource = new CollectionViewSource();
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(1000);
timer.Tick += timer_Tick;
timer.Start();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
The Expander.Header does not get one of your view models as DataContext. Instead the header gets an object that inherits from CollectionViewGroup. One of its properties is Name. That's why you can bind to Name in your XAML
<TextBlock ... Text="{Binding Path=Name,StringFormat= Order Number:# {0}}" />
Another property of interest is Items. That's the list of all view models of that group. Now it's easy to access an item's property in the header
<TextBlock ... Text="{Binding Path=Items[0].MyProperty}" />
How to access a named control that is in the content template of the contentpresenter. how to access the webview control(x:name=detView) from cs file.
<ContentPresenter
x:Name="DetailContentPresenter"
Grid.Row="0"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
Content="{x:Bind coll.SelectedItem,Mode=OneWay}">
<ContentPresenter.ContentTemplate>
<DataTemplate x:DataType="data:coll_Details" x:Name="ttt">
<Grid>
<WebView DefaultBackgroundColor="#F5F5F5" x:Name="detView" Source="ms-appx-web:///Assets/Web/collDetails.html"/>
</Grid>
</DataTemplate>
</ContentPresenter.ContentTemplate>
<ContentPresenter.ContentTransitions>
<TransitionCollection/>
</ContentPresenter.ContentTransitions>
</ContentPresenter>
If you are using ContentPresenter as ControlTemplate like the example of Official Documentation.
You can get the template through the controlName.ContentTemplateRoot. I made a demo from the Example of official documentation above, and put a webview inside the DataTemplate.
MainPage.xaml:
<Page.Resources>
<Style TargetType="HyperlinkButton" x:Key="myStyle" >
...
<Setter Property="Template" x:Name="presenterSetter">
<Setter.Value>
<ControlTemplate TargetType="HyperlinkButton">
<Grid x:Name="rootGrid">
...
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="3">
<ContentPresenter x:Name="MyContentPresenter"
Content="{TemplateBinding Content}"
ContentTransitions="{TemplateBinding ContentTransitions}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
>
<ContentPresenter.ContentTemplate>
<DataTemplate x:Name="ttt">
<Grid>
<WebView Source="ms-appx-web:///Assets/Web/default.html" Name="myWebView"/>
</Grid>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Border>
<!--focus visuals omitted-->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel VerticalAlignment="Bottom">
<HyperlinkButton Name="myHyperlink" Style="{StaticResource myStyle}">This is a link</HyperlinkButton>
<Button Click="Button_Click" Name="myBtn">Click Me</Button>
</StackPanel>
</Grid>
And I can get the WebView using the codes below:
private void Button_Click(object sender, RoutedEventArgs e)
{
var myView= ((Grid)myHyperlink.ContentTemplateRoot).Children[0] as WebView;
}
In C#, use this code to find any control which is present in your ContentPresenter.
If a TextBlock is present in your ContentPresenter then first create a object of the TextBlock then cast it and then find control.
TextBlock myTextBlock = (TextBlock)ttt.FindName(“textBlock”, DetailContentPresenter);
How could I get a DataGrid control used in xaml style template to use it in code?
<UserControl>
<UserControl.Resources>
<Style x:Key="MyComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid x:Name="MainGrid" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0" MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" />
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup"
Grid.ColumnSpan="2">
<Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw">
<Border x:Name="DropDownBorder">
<DataGrid x:Name="PART_PopupDataGrid" />
</Border>
</Microsoft_Windows_Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton Grid.ColumnSpan="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
IsChecked="{Binding Path=IsDropDownOpen,
Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ComboBoxReadonlyToggleButton}" />
<ContentPresenter Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
IsHitTestVisible="false"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<ComboBox Name="ComboGrid" Style="{DynamicResource DataGridComboBoxStyle}" />
</Grid>
</UserControl>
There is how I tried to get DataGrid control in user control, but not successfully:
var v1 = this.FindName("PART_PopupDataGrid");
var v2 = this.Template.FindName("PART_PopupDataGrid", this);
var v3 = ComboGrid.FindName("PART_PopupDataGrid");
How could I get this control in code?
This is a very common requirement. In fact, it's so common that Microsoft even have a page on MSDN specifically for this:
How to: Find ControlTemplate-Generated Elements
Basically, if you have a reference to the ComboBox that the ControlTemplate is applied to (let's call it ComboBox), then you should be able to do this:
DataGrid dataGrid =
ComboBox.Template.FindName("PART_PopupDataGrid", ComboBox) as DataGrid;