i replace the ContentPresenter in the DataGridCell's Template with a TextBlock an now i search for the correct Binding to the content.
The normal way is Text="{TemplateBinding Content} for the TextBlock - it doesn't work. Also Text="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content, Mode=TwoWay}" doesn't work correct.
Any other ideas?
Suppose you have changed the DataGridCell Template to the following
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<TextBlock Text="{Binding}"/>
<!--<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> -->
</Border>
</ControlTemplate>
Since you removed the ContentPresenter, the DataGridCell has no way of displaying its Content. It's still there though. The DataGridCell.Content
is a TextBlock containing your original Text and the TextBlock in the Template is another.
So you'll get the correct Text by binding it to the Content.Text property of the TemplatedParent
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Content.Text}"/>
So, to sum it up. This works
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Content.Text}"/>
</Border>
</ControlTemplate>
The data context of the data grid cell should be the data itself. So the binding should simply be:
<TextBlock Text="{Binding}"/>
Related
I'm completely stuck trying to bind an image to my custom WPF Expander.
I found an examle for creating expander template here: https://www.codeproject.com/Articles/248112/Templating-WPF-Expander-Control and tried to edit this to use an image instead of expander icon.
Here is my custom template for expander button (I added an image source here, so it works properly with straight resource path, not binding):
<ControlTemplate x:Key="SimpleExpanderButtonTemp"
TargetType="{x:Type ToggleButton}">
<Border x:Name="ExpanderButtonBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image
Height="35"
Width="35"
Source="{Binding Path = ImageSource,
RelativeSource={RelativeSource TemplatedParent}}">
</Image>
<ContentPresenter x:Name="HeaderContent"
Grid.Column="1"
Margin="4,0,0,0"
ContentSource="Content"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- MouseOver, Pressed behaviours-->
<Trigger Property="IsMouseOver"
Value="true">
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Afterwards, I add template to expander itself:
<ControlTemplate x:Key="SidePanelExpander" TargetType="Expander">
<DockPanel>
<ToggleButton x:Name="ExpanderButton"
DockPanel.Dock="Top"
ImageSource="{Binding Path = ImageSource,
RelativeSource={RelativeSource TemplatedParent}}"
Template="{StaticResource SimpleExpanderButtonTemp}"
Content="{TemplateBinding Header}"
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource TemplatedParent}}"
OverridesDefaultStyle="True"
Padding="1.5,0">
</ToggleButton>
<ContentPresenter x:Name="ExpanderContent"
Visibility="Collapsed"
DockPanel.Dock="Bottom"/>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="ExpanderContent"
Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
And then I'm trying to use it this way:
<Expander ExpandDirection="Right"
Template="{StaticResource SidePanelExpander}"
ImageSource="../Res/Images/engine.png"
>
I guess there are some difficulties in data binding forwarding through templates, but have no idea on how to solve this.
As Clemens said in the comments, ToggleButton and Expander don't have a property called ImageSource, so this code was never going to work. The "correct" way to do this would be to create a custom control, but a quick fix (hack) would be to specify the image path using the Tag property:
In the "SimpleExpanderButtonTemp" template, change the Image element Source as follows:
<Image Height="35"
Width="35"
Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}" />
Next, in the "SidePanelExpander" template ToggleButton, get rid of the "ImageSource=..." line and replace it with this:
Tag="{TemplateBinding Tag}"
Finally, in the control itself, specify your image:
<Expander Tag="../Res/Images/engine.png"
...
Also, in the first template, I've noticed you use TemplateBindings on certain properties of the Border control (Background, BorderBrush, BorderThickness). These won't work either. To fix this, copy those same lines to the ToggleButton control of the second template, i.e.
<ToggleButton x:Name="ExpanderButton"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
...
You can now specify (say) a background colour in the control itself:
<Expander Background="Blue"
...
The TemplateBindings on the ToggleButton act like stepping stones, allowing the property value to pass from the control itself to the ToggleButton, then on to the Border.
I have a custom TreeListView - based on the standard TreeView - with the following style:
<Style TargetType="{x:Type c:TreeListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type: c:TreeListView}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<GridViewHeaderRowPresenter DockPanel.Dock="Top" Columns="{Binding Path=Columns, RelativeSource={RelativeSource TemplatedParent}}" />
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
As I have no ScrollViewer, the content will not scroll so if the content requires more space than is available, it looks like this:
So I added a ScrollViewer so that the style now Looks like this:
<Style TargetType="{x:Type c:TreeListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type: c:TreeListView}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Focusable="False" Padding="{TemplateBinding Padding">
<DockPanel>
<GridViewHeaderRowPresenter DockPanel.Dock="Top" Columns="{Binding Path=Columns, RelativeSource={RelativeSource TemplatedParent}}" />
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</DockPanel>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
With the content expanded as in the first Image, the Content is not able to scroll, but the column Header scrools as well, which is not wished for :D
Could anyone please help me so that only the content gets scrolled, and not the columns bound to the GridViewHeaderRowPresenter?
Thank you!
[EDIT]
Thanks to the related links to the right, I found the answer here.
Instead of having the DockPanel within the ScrollViewer I needed only to have the ItemsPresenter inside the ScrollViewer. So with the following Style it now works.
<Style TargetType="{x:Type c:TreeListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:TreeListView}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<GridViewHeaderRowPresenter DockPanel.Dock="Top" Columns="{Binding Path=Columns, RelativeSource={RelativeSource TemplatedParent}}" />
<ScrollViewer Focusable="False" Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Many thanks to Nikhil Agrawal for his code!
The solution given is incomplete and will only work if you have a fixed width control. By only wrapping the ItemsPresenter the header columns will not scroll horizontally if needed. This results in shifted value columns which then mismatch the headers.
I've tried applying SelectiveScrollingGrid.SelectiveScrollingOrientation="Horizontal" to GridViewHeaderRowPresenter but haven't been successfull with that approach.
Instead, you could add a scroll viewer to the GridViewHeaderRowPresenter and to the ItemsPresenter and then synchronize them. It's not the best solution, but at least it works.
<Style TargetType="{x:Type local:TreeListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TreeListView}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ScrollViewer DockPanel.Dock="Top"
HorizontalScrollBarVisibility="Hidden"
Name="scrollViewerHeader"
VerticalScrollBarVisibility="Disabled">
<GridViewHeaderRowPresenter Columns="{StaticResource gvcc}"/>
</ScrollViewer>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
Name="scrollViewerBody"
VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
And the code behind:
public class TreeListView : TreeView
{
private ScrollViewer _scrollViewerHeader;
private ScrollViewer _scrollViewerBody;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_scrollViewerHeader = (ScrollViewer)Template.FindName("scrollViewerHeader", this);
_scrollViewerBody = (ScrollViewer)Template.FindName("scrollViewerBody", this);
_scrollViewerBody.ScrollChanged += (sender, e) =>
{
_scrollViewerHeader.Width = e.ViewportWidth;
_scrollViewerHeader.ScrollToHorizontalOffset(e.HorizontalOffset);
};
}
}
The way DataGrid solves it is it customizes the Template of the ScrollViewer to place the GridViewHeaderRowPresenter outside the ScrollViewer ScrollContentPresenter:
https://github.com/dotnet/wpf/blob/059ba38771aef10d2172a3ca0e247dfbfa8cefde/src/Microsoft.DotNet.Wpf/src/Themes/PresentationFramework.Aero2/Themes/Aero2.NormalColor.xaml#L987-L993
This way when the scrollbars scroll, they don't impact the header row.
The ListView that uses GridView does a similar thing here:
https://github.com/dotnet/wpf/blob/059ba38771aef10d2172a3ca0e247dfbfa8cefde/src/Microsoft.DotNet.Wpf/src/Themes/PresentationFramework.Aero2/Themes/Aero2.NormalColor.xaml#L2628
At the same time they wrap the header row in another ScrollViewer, and honestly I don't understand why.
Once I find out more I will expand on this answer. I think this approach if it works is cleaner than synchronizing scroll viewers.
the above tree view is looking with shadow effect on top left and left. I need a single thick line .
my xaml is
Margin="0,0,0,2" BorderBrush="Black" BorderThickness="1"
I need a single line but not a shodow. Can you please help me how to do it ?
One way to do it is to create simple Template for your TreeView like so:
<TreeView BorderBrush="Black" BorderThickness="1" Background="Beige">
<TreeView.Template>
<ControlTemplate TargetType="{x:Type TreeView}">
<Border
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer>
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</TreeView.Template>
</TreeView>
You can try this to get rid of treeview border and apply your own border:
<Border BorderThickness="1" BorderBrush="Black">
<TreeView BorderBrush="Transparent" BorderThickness="0"
</TreeView>
</Border>
I have created my own button based on icrosoft_Windows_Themes2:ButtonChrome. I want to get rid of the default border and i've tried setting it to null or transparent but still i get a white border.
Here's my markup:
<Microsoft_Windows_Themes2:ButtonChrome x:Name="ibAero" Visibility="Collapsed" SnapsToDevicePixels="true" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" RoundCorners="True" Background="{TemplateBinding Background}" BorderBrush="Transparent" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" OverridesDefaultStyle="True">
<Border x:Name="bd" CornerRadius="10" BorderThickness="1" BorderBrush="{Binding ElementName=IB, Path=BorderBrush}">
<StackPanel Orientation="Horizontal" Margin="8,1,5,1">
<TextBlock Text="{Binding ElementName=IB, Path=Text}" Foreground="{Binding ElementName=IB, Path=TextForeground}" Margin="{Binding ElementName=IB, Path=TextMargin}" VerticalAlignment="Center"/>
</StackPanel>
</Border>
</Microsoft_Windows_Themes2:ButtonChrome>
EDIT:
Ok. I've changed the markup like this:
<Border x:Name="ibAero" Visibility="Collapsed" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" CornerRadius="3" BorderThickness="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}">
<StackPanel x:Name="ibAeroPanel" Orientation="Horizontal" Margin="8,1,5,1">
<Image Source="{Binding ElementName=IB, Path=Image}" Width="{Binding ElementName=IB, Path=ImageWidth}" Height="{Binding ElementName=IB, Path=ImageHeight}"/>
<TextBlock Text="{Binding ElementName=IB, Path=Text}" Foreground="{Binding ElementName=IB, Path=TextForeground}" Margin="{Binding ElementName=IB, Path=TextMargin}" VerticalAlignment="Center"/>
</StackPanel>
</Border>
How do i set the default style in my usercontrol?
The answer to this problem is posted by Meleak in this link: How to remove ButtonChrome border (when defining the template of a border)?
The reason you get that white border is because it is added IN CODE in the ButtonChrome control. There is NO WAY to achieve the solution to this with templating. The only way to remove the border is write your own version of the control.
I googled this issue and found numerous answers by people offering template "solutions". None of them will ever possibly work as suggested.
Add Meleak's modified ButtonChrome.cs control to your library of customized WPF controls (ComboBox with disabled F4 key being another in my personal inventory) and you are good to go.
I would recommend not including the Button Chrome in your template. It would require extra work if you want to recreate some of the nice effects it provides on mouseover and pressed, but it will get rid of the border.
I'm trying to make a TextBox template and I need to include a ScrollViewer in the template - basically I want to add some content (like line numbers) that needs to scroll along with the normal text.
The default template for the TextBox is like this:
<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="1" Opacity="1">
<Grid>
<Border x:Name="ReadOnlyVisualElement" Background="#5EC9C9C9" Opacity="0"/>
<Border x:Name="MouseOverBorder" BorderBrush="Transparent" BorderThickness="1">
<ScrollViewer x:Name="ContentElement" BorderThickness="0" IsTabStop="False" Padding="{TemplateBinding Padding}"/>
</Border>
</Grid>
</Border>
If I change the ContentElement from ScrollViewer to Border, for example, the TextBox behaves normally - i just lose the scrolling ability.
Now, if I wrap the ContentElement with a ScrollViewer, it no longer displays the caret and selection - if you type, it still gets updated though.
<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="1" Opacity="1">
<Grid>
<Border x:Name="ReadOnlyVisualElement" Background="#5EC9C9C9" Opacity="0"/>
<Border x:Name="MouseOverBorder" BorderBrush="Transparent" BorderThickness="1">
<ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" >
<Border x:Name="ContentElement" BorderThickness="0" Padding="{TemplateBinding Padding}" />
</ScrollViewer>
</Border>
</Grid>
</Border>
Any idea why this happens and how can I fix it?
Just a shot in the dark, as I have not verified...but the control may be looking for a TemplatePart named ContentElement of type ScrollViewer.