Simple question. How do you disable the text selection of DocumentViewer in WPF? This is the feature where an XPS document is displayed by the viewer and then text can be highlighted via mouse. The highlighted text can also be copied but I have already disabled this. I just don't know how to disable the highlighting.
Thanks!
you may use IsFocusable=false. But search box will be disabled too...
We have solved this by overriding the ControlTemplate of the ScrollViewer embedded in the DocumentViewer control. Insert the Style below in "Window.Resources":
<Style TargetType="{x:Type ScrollViewer}" x:Key="CustomScrollPresenter">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid Background="{TemplateBinding Panel.Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Rectangle Grid.Column="1" Grid.Row="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
<ScrollContentPresenter
PreviewMouseLeftButtonDown="ScrollContentPresenter_PreviewMouseLeftButtonDown"
Grid.Column="0"
Grid.Row="0"
Margin="{TemplateBinding Control.Padding}"
Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}" />
<ScrollBar
x:Name="PART_VerticalScrollBar"
Grid.Column="1"
Grid.Row="0"
Minimum="0"
Maximum="{TemplateBinding ScrollViewer.ScrollableHeight}"
ViewportSize="{TemplateBinding ScrollViewer.ViewportHeight}"
Value="{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Visibility="{TemplateBinding ScrollViewer.ComputedVerticalScrollBarVisibility}"
Cursor="Arrow" AutomationProperties.AutomationId="VerticalScrollBar" />
<ScrollBar
x:Name="PART_HorizontalScrollBar"
Orientation="Horizontal" Grid.Column="0" Grid.Row="1" Minimum="0"
Maximum="{TemplateBinding ScrollViewer.ScrollableWidth}" ViewportSize="{TemplateBinding ScrollViewer.ViewportWidth}" Value="{Binding Path=HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" Visibility="{TemplateBinding ScrollViewer.ComputedHorizontalScrollBarVisibility}" Cursor="Arrow" AutomationProperties.AutomationId="HorizontalScrollBar" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then override the Style of ScrollViewer with it in the ControlTemplate for DocumentViewer:
<Style
x:Key="MyDVStyleExtend"
BasedOn="{StaticResource {x:Type DocumentViewer}}"
TargetType="{x:Type DocumentViewer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DocumentViewer">
<Border BorderThickness="2,2,2,2"
BorderBrush="SlateBlue" Focusable="False">
<Grid Background="{StaticResource GridBackground}"
KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition Width ="*"/>
</Grid.ColumnDefinitions>
<ScrollViewer Style="{StaticResource CustomScrollPresenter}" Grid.Column ="0"
CanContentScroll="True"
HorizontalScrollBarVisibility="Auto"
x:Name="PART_ContentHost"
IsTabStop="True"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then create a function for the "PreviewMouseLeftButtonDown="ScrollContentPresenter_PreviewMouseLeftButtonDown"" attribute stated in the CustomScrollPresenter style.
private void ScrollContentPresenter_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
Another way would be to add e.g. a dockpanel:
<DockPanel Name="pnlTouchTaker"
VerticalAlignment="Bottom" HorizontalAlignment="Left"
Background="Transparent">
</DockPanel>
lying "above" the documentviewer and set it´s width and height to the actual width and height of the scrollviewer content in e.g. page loading event.
You might have to add additional logic if using zoom options and the horizontal toolbar becomes visible.
Implement the following code in xaml.cs part (DocumentViewerInstance x:Name of your DocumentViewer in your xaml.)
DocumentViewerInstance.GetType().GetProperty("IsSelectionEnabled", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(DocumentViewerInstance, false, null);
You can use the IsFocusable=false or IsHitTestVisible = false or handle any preview event (for example in accepted answer) for disable selection but the hyperlinks won't work! If you set IsSelectionEnabled = false, the selection will be disabled but hyperlinks will work too. (Warning! IsSelectionEnabled can be changed for true value after you set false, so you should check the value often.)
Related
I've created a basic Windows Desktop WPF Application. In the MainWindow, I've added the following as the body of the window:
<ScrollViewer Template="{DynamicResource ScrollViewerControlTemplate1}">
<ScrollViewer.Resources>
<ControlTemplate x:Key="ScrollViewerControlTemplate1" TargetType="{x:Type ScrollViewer}">
<Grid x:Name="Grid" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
<ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" SmallChange="40000"/>
<ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
</Grid>
</ControlTemplate>
</ScrollViewer.Resources>
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="300"/>
<RowDefinition Height="300"/>
<RowDefinition Height="300"/>
<RowDefinition Height="300"/>
<RowDefinition Height="300"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="SteelBlue"/>
<Border Grid.Row="1" Background="Peru"/>
<Border Grid.Row="2" Background="Goldenrod"/>
<Border Grid.Row="3" Background="Tomato"/>
<Border Grid.Row="4" Background="IndianRed"/>
</Grid>
</ScrollViewer>
You'll notice that on PART_VerticalScrollbar, I've set the SmallChange="40000" (an arbitrarily large number). Yet when I click the up/down arrows on the scrollbar, it does the same very small change as it did before I set the SmallChange to anything.
I've read over the documentation a number of times, and can't figure out why this isn't having any effect on the amount that the ScrollViewer scrolls. Any ideas?
Note that I could change the ScrollBar template and change the command these button calls to Scrollbar.PageUpCommand rather than Scrollbar.LineUpCommand, but ultimately I'd like to have finer control over the scrolling than a full page.
The reason for this is a special logic implemented in the ScrollViewer and in the ScrollBar.
The ScrollBar.SmallChange property will be only considered for scrolling if the scroll bar is stand-alone. That means, when it is outside of a ScrollViewer.
If you look at the ScrollViewer.OnApplyTemplate method, you will notice the following:
public override void OnApplyTemplate()
{
// ...
ScrollBar scrollBar = GetTemplateChild(HorizontalScrollBarTemplateName) as ScrollBar;
if (scrollBar != null)
scrollBar.IsStandalone = false;
// Same for the vertical scroll bar
// ...
}
If a ScrollViewer finds scroll bars inside of it, it sets their IsStandalone (internal) properties to false, which disables the scroll command handing of the scroll bars. Instead, the ScrollViewer takes over the scroll command handing, but it ignores some ScrollBar's properties, including the SmallChange property.
TL;DR This won't work for the scroll bars inside of a ScrollViewer. You can change your template so use two "external" scroll bars. But then, you will need to connect those scroll bars with the ScrollViewer manually.
I suspect that your custom control template isn't being used because of the way you are referencing it. I would change the XAML to this:
<ScrollViewer>
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid ...>
...
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
... ScrollViewer content goes here
</ScrollViewer>
Basically, set the control's template directly, rather than using a dynamic resource.
Basically, I'd like to make the WPF DataGrid control layout its columns exactly the way the WinForms DataGridView does
And more specifically, here is the behaviour I'm looking for:
The grid control should take up the space it's given (i.e. however much space is available in its parent control for it to use). Here I am referring just to the control, and not to the columns.
The columns created (whether automatically or manually) may or may not take up all this space.
If there is extra space left over after the columns are created, the last column should not be expanded to fill this space
If there is extra space left over after the columns are created, an empty column with nothing in it should not be created to fill this extra space
From what I can tell, in WPF the last two bullet points seem to be mutually exclusive and you must choose one or the other. Has anyone found a way to do both? I've searched quite a bit and haven't found quite what I'm looking for, however all the posts I'm finding tend to be a couple years old so I'm hoping someone has figured this thing out by now.
EDIT: sa_ddam213, here's a quick xaml project I put together to test your suggestion.
<Window x:Class="DataGridFix.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataGridFix"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="data"
ObjectType="{x:Type local:TestObject}"
MethodName="GetTestData" />
</Window.Resources>
<StackPanel>
<DataGrid HorizontalAlignment="Left" ColumnWidth="Auto" Height="150" VerticalAlignment="Top" ItemsSource="{Binding Source={StaticResource data}}" />
</StackPanel>
</Window>
And here's the code behind:
namespace DataGridFix
{
public class TestObject
{
public int Id { get; set; }
public string Name { get; set; }
public static List<TestObject> GetTestData()
{
var items = new List<TestObject>();
items.Add(new TestObject() { Id = 1, Name = "Joe" });
items.Add(new TestObject() { Id = 2, Name = "Matt" });
items.Add(new TestObject() { Id = 3, Name = "Hal" });
return items;
}
}
}
Really the only noteable thing I see from your suggestion is to set HorizontalAlignment to Left. I did that, and tried setting the ColumnWidth to the various settings but had the same problem with each (except * of course... technically I can mess that one up to but I won't go into that).
If you use your mouse to expand any of the columns, and then decrease the column size then the empty filler column appears. The only other difference I noted from your post was that you put your DataGrids in a StackPanel since you had more than one of them. I tried that just for the heck of it but same result. If you see any other difference between what I'm doing and what you suggested please let me know.
In order to get the behavior you want, you probably have to modify the control template of the DataGrid.
Take a look at the code. I have gotten pretty close to the WinForms DataGridView look i think.
To remove the extra column you have to remove the filler column from the DataGridColumnHeadersPresenter. I have just commented it out. The rest is just the default template.
The other modification is to the template of the DataGrid.
By setting HorizontalAlignment="Left" on the ScrollContentPresenter, the rows no longer take up all the width of the control.
Those are the only 2 changes I have made to the default templates.
<Style TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<!-- Remove this filler column -->
<!--<DataGridColumnHeader x:Name="PART_FillerColumnHeader" IsHitTestVisible="False" />-->
<ItemsPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type DataGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True"
Padding="{TemplateBinding Padding}">
<ScrollViewer Focusable="false" Name="DG_ScrollViewer">
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Command="{x:Static DataGrid.SelectAllCommand}"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=CellsPanelHorizontalOffset}"
Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type DataGrid}, ResourceId=DataGridSelectAllButtonStyle}}"
Focusable="false"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.All}}" />
<DataGridColumnHeadersPresenter Grid.Column="1" Name="PART_ColumnHeadersPresenter"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Column}}"/>
<!-- Set HorizontalAlignment="Left" to have the rows only take up the width they need and not fill the entire width of the DataGrid -->
<ScrollContentPresenter HorizontalAlignment="Left" x:Name="PART_ScrollContentPresenter" Grid.Row="1" Grid.ColumnSpan="2" CanContentScroll="{TemplateBinding CanContentScroll}" />
<ScrollBar Grid.Row="1" Grid.Column="2" Name="PART_VerticalScrollBar"
Orientation="Vertical"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<Grid Grid.Row="2" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=NonFrozenColumnsViewportHorizontalOffset}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollBar Grid.Column="1"
Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Value="{Binding Path=HorizontalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
UPDATE
It does indeed look like there is a difference between .NET 4 and .NET 4.5.
I develop on a Windows 8 machine with Visual Studio 2012, so as a test I tried targeting .NET 4 to see if I could replicate the wrong behavior. But it still worked fine.
To be sure, I tried running the app on a different machine with only .NET 4 installed, and here the empty rows showed up when making a column bigger and then smaller again.
The issue seems to be that the DataGridRows are not behaving properly. When running on a machine with only .NET 4 installed, they keep their current size when making the column smaller. On .NET 4.5 they resize as expected.
The new solution to get the behavior you need, is actually much simpler than the previous one.
By simply setting the HorizontalAlignment on the DataGridRows to left, and removing the filler column, it works on both .NET 4 and .NET 4.5. And there is no longer a need to replace the entire template of the DataGrid.
<Style TargetType="DataGridRow">
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Style TargetType="DataGridColumnHeadersPresenter">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<ItemsPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
There ar plenty of layout options for Columns in WPF, its just a matter of choosing what you want to be displayed.
Pixel
SizeToCells
SizeToHeader
Auto
Proportional(*)
And if you set the HorizontalAlignment to Left the DataGrid will resize to fit its contents based on the ColumnWidth you picked.
Here is a example of some of the avaliable column settings
<StackPanel>
<DataGrid HorizontalAlignment="Left" ColumnWidth="100" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="SizeToCells" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="SizeToHeader" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="Auto" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="*" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
</StackPanel>
I need to change viewport of datagrid to maximum so all rows are propagated in constructor. So I used Scrollview with row height set to auto.
<ScrollViewer>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<DataGrid>
</DataGrid>
</Grid>
</ScrollViewer>
This propagates all controls but now when I use scrollbar it also moves header row. I need that the scrollbar will effect only datagrid.
I dont understand this statement
... need to change viewport of datagrid to maximum so all rows are
propagated in constructor. So I used Scrollview with row height set to
auto...
... where is the Viewport setting in the code? Also what is this constructor? Your terminologies are confusing....
However I can assume what you want is the datagrid to fully render its rows (without extra space below rows) and occupy that entriely over the grid panel. But because you have used a scroll viewer abone grid, it will scroll datagrid headers as well.
If you use "Snoop" you will find that wpf datagrid has a scroll viewer as its visual child and it has a Grid panel in its own template, there the ScrollContentPresenter is under a Grid.Row which has RowDefinition that has Height as asterick (*).
Using visual children extraction method given below, accesss that Grid descendent and change its height to auto.
(GetVisualChild<Grid>(myDataGrid)).RowDefintions[1].Height="Auto"
Method to snoop through all children of a parent...
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null) child = GetVisualChild<T>(v);
if (child != null) break;
}
return child;
}
The XAML way
Override the dataGrid Control template...
<Style TargetType="{x:Type DataGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True"
Padding="{TemplateBinding Padding}">
<ScrollViewer Focusable="false"
Name="DG_ScrollViewer">
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <!--This changed to Auto from '*'-->
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!--Left Column Header Corner -->
<Button Command="{x:Static dg:DataGrid.SelectAllCommand}"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=CellsPanelHorizontalOffset}"
Template="{StaticResource SelectAllButtonTemplate}"
Focusable="false"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=HeadersVisibility, Converter={x:Static dg:DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static dg:DataGridHeadersVisibility.All}}" />
<!--Column Headers-->
<dgp:DataGridColumnHeadersPresenter Grid.Column="1"
Name="PART_ColumnHeadersPresenter"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=HeadersVisibility, Converter={x:Static dg:DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static dg:DataGridHeadersVisibility.Column}}"/>
<!--DataGrid content-->
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" Grid.Row="1" Grid.ColumnSpan="2" CanContentScroll="{TemplateBinding CanContentScroll}" />
<ScrollBar Grid.Row="1" Grid.Column="2" Name="PART_VerticalScrollBar"
Orientation="Vertical"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<Grid Grid.Row="2" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type dg:DataGrid}}, Path=NonFrozenColumnsViewportHorizontalOffset}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollBar Grid.Column="1"
Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Value="{Binding Path=HorizontalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
what's the element name in the green circle? Whta's the event corresponding to click this element?
Interestingly, this part of the DataGrid is no-man's-land - it doesn't even have an official name, let alone a behaviour or a template. In order to handle events related to this area, see here or here.
I call it the 'select all' button. However, it doesn't have an official name. It also does not have any style properties exposed. For a simple method to style it via attached properties, see the following:
http://www.scottlogic.co.uk/blog/colin/2009/02/styling-hard-to-reach-elements-in-control-templates-with-attached-behaviours/
Colin E.
As others have stated, it's the "Select All" Button. Clicking it will select everything in the DataGrid if SelectionMode is set to Extended.
The "Select All" Button is part of the DataGrid Template. Or actually, it's part of the ScrollViewer Template that is part of the DataGrid Template so it's nested pretty deep. The ControlTemplate looks like this. From here you can do anything you want with the SelectAll Button, disable it, change the Background etc.
<Style TargetType="{x:Type DataGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="DG_ScrollViewer" Focusable="false">
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Here it is -->
<Button Command="{x:Static DataGrid.SelectAllCommand}" Focusable="false" Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}}" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.All}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
<DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Grid.Column="1" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" Grid.ColumnSpan="2" Grid.Row="1"/>
<ScrollBar x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Grid.Row="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
<Grid Grid.Column="1" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollBar x:Name="PART_HorizontalScrollBar" Grid.Column="1" Maximum="{TemplateBinding ScrollableWidth}" Orientation="Horizontal" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
</Grid>
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If you want to disable the selectall button you can also do like this:
//Disable select all button
datagrid.CommandBindings.Add(new CommandBinding(DataGrid.SelectAllCommand, delegate { }, (s, a) => { if (a.OriginalSource is Button) ((Button)a.OriginalSource).IsEnabled = false; }));
as a workaround.
This can also be used to wire other things up.
The "problem" with re-templating is that you have to take all the themes into account... tedious :)
In at least one example I found, clicking the gray triangle in the corner causes all rows to be selected.
But this is not a built-in feature of this control as far as I know. It is part of a custom template.
See this example:
http://www.dotnetspark.com/kb/2941-styling-wpf-datagrid.aspx
If you download the sample, and search the Resources/DataGridCustomization.xaml file for "SelectAll", you'll find the part of the template that contains this feature.
I have created a static resource defining the border of a specific item in my xaml, but I can't find a good way to define a unique color for each side!
xaml:
<Border Style="{StaticResource SidePanelBorder}">
<!-- rest of the xaml -->
</Border>
StaticResource:
<Style x:Key="SidePanelBorder">
<Setter Property="Control.BorderBrush" Value="#FF363636" />
<Setter Property="Control.BorderThickness" Value="1" />
</Style>
But I want to define one color for each side of the border, and eventually also a different Border thickness.
Any good techniques out there doing this?
Seems very hacky, but you could define borders within borders, and make only 1 side have a thickness. For example
<Border BorderThickness="0,0,0,10" BorderBrush="Green">
<Border BorderThickness="0,0,10,0" BorderBrush="Blue">
<Grid>
<Button>Hello</Button>
</Grid>
</Border>
</Border>
would give a green border on the bottom and a blue border to the right. Isn't the prettiest piece of Xaml though.
Another solution using one Border and a VisualBrush, allowing setting the Border's CornerRadius and BorderThickness:
<Border BorderThickness="10" CornerRadius="10" HorizontalAlignment="Right" Height="150" VerticalAlignment="Bottom" Width="150" Margin="0,0,92.666,42.667">
<Border.BorderBrush>
<VisualBrush>
<VisualBrush.Visual>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Path x:Name="ColoredBorderLeft" Data="M0,0 L0,0 1,0.5 L1,0.5 0,1" Fill="Blue" Stretch="Fill" Grid.RowSpan="2"/>
<Path x:Name="ColoredBorderRight" Data="M1,0 L1,0 0,0.5 L0,0.5 1,1" Fill="Red" Stretch="Fill" Grid.Column="1" Grid.RowSpan="2"/>
<Path x:Name="ColoredBorderTop" Data="M0,0 L0,0 0.5,1 L0.5,1 1,0" Fill="Green" Stretch="Fill" Grid.ColumnSpan="2"/>
<Path x:Name="ColoredBorderBottom" Data="M0,1 L0,1 0.5,0 L0.5,0 1,1" Fill="Yellow" Stretch="Fill" Grid.Row="1" Grid.ColumnSpan="2"/>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Border.BorderBrush>
</Border>
The Grid is needed to prevent the tips of the triangle Paths to "push through" into the border.
The Path.Name's can be used for DataBinding or setting the color from code behind.
you can have a DockPanel and can put 4 Borders inside it, each docked to different side.
like:
<DockPanel LastChildFill="true">
<Border DockPanel.Dock="Left" Background="Red"/>
<Border DockPanel.Dock="Top" Background ="Blue"/>
<Border DockPanel.Dock="Right" Background ="Yellow"/>
<Border DockPanel.Dock="Bottom" Background ="Green"/>
<Grid>
...........your control here
</Grid>
</DockPanel>
If you use a Grid you can have Border's overlay on one another to achieve the same affect. Just set the border thickness of the border color you want to show and have the other border thickness be 0.
<UserControl.Resources>
<Style x:Key="GreenBorder" TargetType="Border">
<Setter Property="BorderBrush" Value="Green" />
<Setter Property="BorderThickness" Value="1,1,1,0" />
</Style>
<Style x:Key="RedBorder" TargetType="Border">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.Row="0" Style="{StaticResource GreenBorder}">
<!-- Content goes here -->
</Border>
<Border Grid.Column="0" Grid.Row="0" Style="{StaticResource RedBorder}">
</Border>
</Grid>
This will give a Green border to the left, top and right borders, but a Red border to the bottom border of the Grid cell.
there's no easy way to do this without writing your own control or subclassing border