Accessing element attribute value from its template - c#

I have a combobox defined in xaml:
<ComboBox Width="100"/>
This ComboBox, along with all other combobxes I have, is styled with a ControlTemplate which I copied and edited some colors and such in it.
<ControlTemplate TargetType="{x:Type ComboBox}">....
In this control template, how can I access the value of the Width attribute from the element above?
So for example:
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid Width="??{Binding WidthValue}??" >....
Where the {Binding WidthValue} is 100, from the Width="100" above.

<Grid Width="{TemplateBinding Width}">
P.S. you'll often see this used in the default control templates for controls for attributes like Padding, Margin, and SnapsToDevicePixels

You can use TemplatedParent binding
<Grid Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}">
but you shouldn't need to as Grid should stretch so if you limit ComboBox to 100 you should automatically limit Grid inside
EDIT
If you need to bind to width then I would suggest to bind to ActualWidth instead of Width
<Grid Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualWidth}">
as Width does not need to be defined in all controls where this template is used

Related

C# WPF DataGrid Column Header fitting TextBox into width

I've a DataGrid in my C# WPF application and styled the Columns Header with a Label/TextBlock and a TextBox as an instant filter beneath of it.
My target is to fit the width of the TextBox into the width of the DataGridColumn, but I didn't found an acceptable way for me.
Currently I'm using a Viewbox, but this crashes all size measurements of the elements in the header.
Here is an example what it looks like with the Viewbox:
As you can see, I've either a too big TextBox and FontSize or a too small TextBox.
My currently code looks like that:
<DataGridTemplateColumn.Header>
<Viewbox StretchDirection="DownOnly">
<StackPanel>
<TextBlock Text="Mitarbeiter"/>
<TextBox x:Name="txtInstantSearchPersonio" HorizontalAlignment="Stretch" TextChanged="evh_InstantFilterChanged"/>
</StackPanel>
</Viewbox>
</DataGridTemplateColumn.Header>
Is there a way to fit the TextBox into full wifth of the column but don't stretch the height or fontsize?
You could use a Style for DataGridColumnHeader to fit the Textbox inside of a column
<DataGridTemplateColumn Width="200">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.Header>
<StackPanel>
<TextBlock Text="Mitarbeiter"/>
<TextBox x:Name="txtInstantSearchPersonio" HorizontalAlignment="Stretch" TextChanged="evh_InstantFilterChanged"/>
</StackPanel>
</DataGridTemplateColumn.Header>
Tried by desperation results in an really simple solution.
I put both elements into a StackPanel:
<DataGridTemplateColumn.Header>
<StackPanel>
<TextBlock Margin="0" Padding="0" Text="Mitarbeiter"/>
<TextBox HorizontalAlignment="Stretch" TextChanged="evh_InstantFilterChanged"/>
</StackPanel>
</DataGridTemplateColumn.Header>
and set the DataGrid.ColumnHeaderStyle simply to this:
<Style x:Key="style_DataGridColumnHeader" TargetType="DataGridColumnHeader">
<Style.Setters>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style.Setters>
</Style>
Works fine for me, the TextBox has always full width of the column header.

How to change dependency property behavior in UserControl

I have a WPF user control which contains a TextBox and some buttons. Now I want to change the way in which the VerticalContentAlignment property of the user control is handled in order to change only the vertical content alignment of the contained text box and not of the user control itself. So how can I override the VerticalContentAlignment dependency property of the user control in order to achieve my desired behavior?
You could write a ControlTemplate for the UserControl that simply ignores the VerticalContentAlignment property. Then bind the TextBox's VerticalAlignment to the VerticalContentAlignment property of the UserControl, e.g. by a RelativeSource Binding.
<UserControl ...>
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</UserControl.Template>
<Grid Background="AliceBlue">
<TextBlock
Text="Hello"
VerticalAlignment="{Binding VerticalContentAlignment,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</Grid>
</UserControl>
Test case:
<Grid>
<local:MyUserControl
Height="100"
VerticalAlignment="Center"
VerticalContentAlignment="Bottom"/>
</Grid>

WPF How to create a grid that can scroll with static column and row header

I have created a custom grid with a column and row header. The cells in the grid are empty. What I am trying to do is to create an excel like behavior for it in regards to scrolling. I need to add a horizontal and vertical scrollbar. When you scroll vertically I need the column header to stay in place and the row header to scroll. When you scroll horizontally I need the row column to stay in place and the column header to scroll. Here is the code I currently have for the grid:
<Canvas x:Name="canvas_blueprint" ClipToBounds="True" Width="{Binding ActualWidth, ElementName=canvas}" Height="{Binding ActualHeight, ElementName=canvas}">
<DockPanel>
<Grid x:Name="grid_column_header" DockPanel.Dock="Top" Height="25" Width="{Binding ActualWidth, ElementName=canvas}"></Grid>
<Grid x:Name="grid_row_header" DockPanel.Dock="Left" Width="25" Height="{Binding ActualHeight, ElementName=canvas}" Margin="0,-25,0,0"></Grid>
<Grid x:Name="grid_blueprint" DockPanel.Dock="Right" Height="{Binding ActualHeight, ElementName=image_blueprint}" Width="{Binding ActualWidth, ElementName=image_blueprint}">
<Grid.Background>
<ImageBrush x:Name="imagebrush_blueprint"/>
</Grid.Background>
</Grid>
</DockPanel>
</Canvas>
Basically it is a canvas with a dockpanel that contains 3 grids. 2 grids for the headers and 1 grid for the inside cells. The cells are created dynamically.
I am making a custom grid because I need to have a background image overlayed on the grid. I tried to use free spreadsheet controls, but have not found one that can have a background image. The only other behavior that my grid needs to have is the ability to select a cell, or a range of cells.
I have tried scrollbars and scrollviewers with the grid, but that makes the lines wonky and uneven. I'm not new to WPF programming, but I am nowhere close to expert. Any help would be appreciated.
I would just use a simple DataGrid. It already freezes headers automatically, and you can freeze columns via the FrozenColumnCount property.
You can have a background image by setting the Background property to an ImageBrush.
Selecting ranges can be controlled by setting SelectionMode to DataGridSelectionMode.Extended.
Here is how you would apply a special background color, and other types of formatting, to individual cells:
<DataGrid.CellStyle>
<Style TargetType="{x:Type wpf:DataGridCell}">
<Setter Property="Background" Value="{Binding SomeColorProperty, Converter={StaticResource cellBackgroundConverter}}" />
<Setter Property="FontWeight" Value="{Binding SomeWeightProperty, Converter={StaticResource cellWeightConverter}}" />
.....
</Style>
</DataGrid.CellStyle>

WPF Binding TextBox width to grid column within a list box

I'm trying to build a simple (in terms of design) screen which will allow users to enter multiple rows of data.
The format of the screen is 4 columns (TextBox, TextBox, ComboBox, TextBox), with new rows added dynamically.
I have tried 2 approaches initially - one using a DataGrid, and one using a ListView.
The DataGrid caused many headaches with items retaining focus when they were clicked out of, and the ListView prevented me from being able to consistently access the underlying cell, instead returning the bound data object represented by the row.
My current approach uses a custom component which represents a row, and contains the 3 TextBox objects, and one ComboBox object.
One or more of these objects are displayed in a ListBox.
This approach allows for handling events more consistently, but is visually less straightforward to get working.
Currently, when a row is displayed, the first 3 columns in the grid (which have an explicitly defined width) display fine, but the textbox in the final column does not expand to fill the available width.
The relevant code for the panel:
<DockPanel LastChildFill="True" Name="basePanel">
<Grid x:Name="baseGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="55"></ColumnDefinition>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Name="txtYear" Grid.Column="0" Style="{StaticResource txtComponentStyle}" Text="{Binding Path=RatingYear, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtArrears" Grid.Column="1" Style="{StaticResource txtComponentStyle}" Text="{Binding Path=ArrearsAmount, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox Name="cmbChangeCode" Grid.Column="2" ItemsSource="{Binding Path=ChangeCodes, Mode=OneTime}" SelectedItem="{Binding Path=ChangeCode}" />
<TextBox Name="txtComments" Grid.Column="3" Style="{StaticResource txtComponentStyle}" Text="{Binding Path=Comments, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" />
</Grid>
</DockPanel>
When I select a row in the ListBox, I can see that the object itself fills the entire width, yet the text box in the final column only expands to fit the content.
Setting the width in the column definition to * should (as I understand it) cause the column to expand to fille the available space, and setting the HorizontalAlignment property on the textbox to Stretch should cause the text box to fill the column.
The code below creates the ListBox.
<UserControl.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="0.5" Margin="1">
<DockPanel LastChildFill="True" HorizontalAlignment="Stretch" Background="DarkSlateBlue">
<Controls:ArrearsListEntry />
</DockPanel>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<DockPanel LastChildFill="True" Background="Red">
<ListBox IsSynchronizedWithCurrentItem="True" Name="lstRowData" ItemsSource="{Binding Path=Rows}" />
</DockPanel>
Is there a way to bind the width of the text box to the actual width of the column, or a property which will cause the text box to automatically expand to the available width?
The ListBoxItem's Content is left aligned by default. Add a Setter for the HorizontalContentAlignment to your ListBoxItem Style:
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="ContentTemplate">
...
</Setter>
</Style>

WPF What is the difference between setting the background to transparent and setting opacity on the control?

So I realized that my graph viewer had the axis displaying over the actual items in the graph, so I changed the ZIndex on the grid to display the items over the axis instead.
However, I noticed that I couldn't actually see anything under the actual items because the background of the items were opaque. I think I have two options then, to either set the background of the items to transparent, or to set the opacity of the items. Is there any difference between these two options?
<Grid
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ZIndex="1"
>
<Components:SignalGraphAxis
x:Name="signal_axis"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
GraphHeight="{Binding Path=GraphHeight}"
PenColor="{Binding Path=AxisColor, Mode=OneWay}"
PenWidth="{Binding Path=GraphPenWidth, Mode=OneWay}"
MinHeight="10"
MinWidth="10"
AxisTimeScale="{Binding Path=GraphTimeScale}"
NumberOfPixelsPerDivision="{Binding Path=NumberOfPixelsPerDivision, Mode=OneWay}"
MinDisplayValue ="{Binding Path=MinDisplayValue, Mode=OneWay}"
UnitsOfGraphTimePerInch="{Binding Path=UnitsOfTimePerInch, Mode=OneWay}"
/>
</Grid>
<ScrollViewer
x:Name="signal_scrollviewer"
Grid.Row="1"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.ZIndex="2"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
CanContentScroll="True"
Style="{StaticResource SignalScrollViewerStyle}"
>
<ItemsPresenter />
</ScrollViewer>
</Grid>
Background property is defined on Control class and Opacity is defined much higher on UIElement.
From MSDN Page Control.Background Property
This property only affects a control whose template uses the
Background property as a parameter. On other controls, this property
has no impact.
Let's try to create a Custom Control to see how this works.
CustomControl1.cs
public class CustomControl1 : ContentControl
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
}
Default Template For CustomControl1
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="My Custom Control " Grid.Row="0" />
<ContentPresenter Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Please note, the above template doesn't use Background property at all in it's Template.
Now, Let's try to use that in a Form and see how it behaves:
Code from Window1.xaml
<Grid>
<wpfApplication5:CustomControl1 Background="Green">
<Button Content="Button Within Custom Control" Margin="25"/>
</wpfApplication5:CustomControl1>
</Grid>
The resultant output:
See, there was no Green background for the rendered CustomControl even though we set the Background to Green in Window1.xaml.
Now, Lets modify the template to use Background property.
Template with Background Property
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Grid Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="My Custom Control " Grid.Row="0" />
<ContentPresenter Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the resultant output will now have a the specified background applied.
I think (couldn't find any references though), Opacity gets applied to the element/Control whether the Control's Template worry about Opacity property or not.
Window1.xam with Opacity Set on CustomControl
<Grid>
<wpfApplication5:CustomControl1 Background="Green" Opacity="0.2">
<Button Content="Button Within Custom Control" Margin="25"/>
</wpfApplication5:CustomControl1>
</Grid>
and resultant Output
See, the Opacity got applied even though our Custom Control's template doesn't worry anything about Opacity property.
Finally, to answer your question: Though either setting Opacity to 0 or Background to Transparent may give you the same visual result. But, for Background property, it totally depends on the Control implementation and how it handles Background property. Whereas, with Opacity it gets applied from parent elements to down the elements tree to child elements irrespective of the control.
Refer to MSDN Page, UIElement.Opacity Property to read more on Opacity property and how it behaves when Opacity is set at multiple levels in an element tree.

Categories