WPF centered text in stretched label fixed width - wpf

My goal is simpel, however i can't figure it out. Sorry for the Titel but couldn't come up with a better explanation...
I have usercontrol with an label that display's the current time (hooked up to a timer with 1 second interval). The label is the width of its parent and the text is aligned in the center. The format is DateTime.ToString("HH : mm : ss"), the FontFamily and size can be adjusted by the user. So far nothing strange... But, the text is aligned centered so when time is lets say 12:34:02 the pixel width different than 12:34:11. (of course depending on the font) This causes the label jump (because it auto centers itself)
The code below is an example of it. the canvas is used to draw stuff on it and the viewbox is used so it autosizes itself in his parent.
Code:
<Grid>
<Viewbox>
<Canvas Name="canv" Height="300" Width="300">
<StackPanel Name="stckpnlDateTime">
<Label Name="lblDateOrText"
Grid.Column="0"
Grid.Row="0"
Content = "------"
FontSize="25"
Foreground="GhostWhite"
HorizontalContentAlignment="Center"
VerticalAlignment="Bottom"
FontFamily="Arial"
Width="Auto"/>
<Label Name="lblTime"
Grid.Column="0"
Grid.Row="1"
Content = "-- : -- : --"
FontSize="25"
Foreground="GhostWhite"
HorizontalContentAlignment="Center"
VerticalAlignment="Top"
FontFamily="DS-Digital"
Width="Auto"/>
</StackPanel>
</Canvas>
</Viewbox>
</Grid>
Private Sub SystemTime_Tick(sender As Object, e As EventArgs) Handles tmrSystemTime.Tick
lblTime.Content = Now.ToString("HH : mm : ss")
End Sub
So i tried a different approach, great a grid with 10 columns and 8 labels, one for each char, and stretch the labels to its parent (cell). This works and keeps the chars on a fixed position. But the width of last column is smaller then the rest... In this image you can see hat i mean, the second purple column is what i mean. Example alignment
Code:
<UserControl.Resources>
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="Foreground" Value="White" />
<Setter Property="FontFamily" Value="DS-Digital" />
<Setter Property="FontSize" Value="40"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Right"/>
<Setter Property="Background" Value="Green" />
</Style>
</UserControl.Resources>
<Grid HorizontalAlignment="Stretch">
<Viewbox HorizontalAlignment="Stretch">
<Canvas Name="canv" Height="300" Width="300" HorizontalAlignment="Stretch">
<StackPanel Name="stckpnlDateTime" HorizontalAlignment="Stretch">
<Label Name="lblDateOrText"
Grid.Column="0"
Grid.Row="0"
Content = ""
FontSize="25"
Foreground="GhostWhite"
HorizontalContentAlignment="Center"
VerticalAlignment="Bottom"
FontFamily="Arial"
Width="Auto"/>
<Grid Name="GridTimeLabel" HorizontalAlignment="Stretch" Width="Auto" Grid.Column="0" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Label Background="Purple" Grid.Column="0" Grid.Row="0"/>
<Label Name="lblTime1" Grid.Column="1" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
<Label Name="lblTime2" Grid.Column="2" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
<Label Name="lblTime3" Grid.Column="3" Grid.Row="0" Style="{StaticResource LabelStyle}" Content=":"/>
<Label Name="lblTime4" Grid.Column="4" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
<Label Name="lblTime5" Grid.Column="5" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
<Label Name="lblTime6" Grid.Column="6" Grid.Row="0" Style="{StaticResource LabelStyle}" Content=":"/>
<Label Name="lblTime7" Grid.Column="7" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
<Label Name="lblTime8" Grid.Column="8" Grid.Row="0" Style="{StaticResource LabelStyle}" Content="-"/>
<Label Background="Purple" Grid.Column="9" Grid.Row="0"/>
</Grid>
</StackPanel>
</Canvas>
</Viewbox>
</Grid>
Long story short, i'm stuk.... hopefully someone could point me in the right direction.

I have two methods in mind.
First method:
Change to a monospace font, this will ensure that no matter what time value you throw at it, the width will be constant. But you may not find a good/suitable font using this method, though.
Second method:
If you are not using MVVM, try this (C# code, I'm not too used to VB.net):
// Code-behind
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var label = this.lblTime;
label.Text = "00:00:00";
label.Measure(); // Force it to measure
label.Width = label.DesiredSize.Width;
}
This will force the width to stay constant. Depending on the font, you need to manually set the time value that takes up the most space.
Also, to be sure it works properly, you may need to wrap the Label in a Grid. Make the Grid have 3 columns, set the label in column 1 (middle column), and set the columndefinitions' widths to *, auto and * respectively in that order.

Your Canvas has a width of 300. It looks to me as there is less than 300 px available. This is why the last column is smaller.

#Jai, thanks for pointing me in the direction of the .Measure() Sub. After fiddling around i ended up with the 3 column grid, measuring the size of the label with the new content, setting the size of the label. This causes the Column to realign, which holds the label in place.
The code: (created new WPF program for the test, The colors are to see the difference between child and parent)
<Grid Background="Tomato">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Name="lblTime"
Grid.Column="1"
Grid.Row="0"
Content = "-- : -- : --"
FontSize="50"
Foreground="Black"
Background="Beige"
HorizontalContentAlignment="Left"
VerticalAlignment="Center"
FontFamily="DS-Digital"/>
</Grid>
And the code behind it:
Class MainWindow
''' <summary>
''' Timer for updating the time Clock
''' </summary>
Dim WithEvents tmrSystemTime As New DispatcherTimer With {.Interval = TimeSpan.FromSeconds(1)} 'Set Timer interval on every second.
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
tmrSystemTime.Start()
End Sub
Private Sub SystemTime_Tick(sender As Object, e As EventArgs) Handles tmrSystemTime.Tick
'Set the time in the label
lblTime.Content = Now.ToString("HH : mm : ss")
'Measure and set the size of the label
MeasureSizeTimeLabel()
End Sub
''' <summary>
''' Measure the Max Size of the label with a specific Format
''' </summary>
Private Sub MeasureSizeTimeLabel()
'Store the Max size of the Time Label in this variable
Dim MaxClockSize As Size
'Measure the Max size of the clock label and use this width
'lblTime.Content = "00 : 00 : 00"
lblTime.Measure(New Size(Double.PositiveInfinity, Double.PositiveInfinity))
MaxClockSize = lblTime.DesiredSize
'Now Set the size of the label
lblTime.Width = MaxClockSize.Width
lblTime.Height = MaxClockSize.Height
End Sub
Thanks all for the help, appreciate the effort and time! :-)

Related

WPF: Calculating the size of a Grid Column

I have a grid that contains three columns.
<Grid Background="AliceBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="2*" Name="ManualControlsSplit" />
</Grid.ColumnDefinitions>
The first column contains a Grid and a chart.
The second column contains a GridSplitter.
The third column contains a StackPanel which contains a number of TextBlocks, Buttons and Grids containing TextBlocks and Buttons. Text size is dynamic and based on translation resources.
I need to calculate the minimum width that the contents of the third column would ideally like to be able to be drawn into so that the contents are not clipped.
My knowledge of WPF is limited to what I can google so any help would be appreciated.
With layout in WPF, you can allow one if not more of the Grid.Columns to be <ColumnDefinition Width="Auto"/>. This essentially tells WPF to allow as much space as the contained controls want. The use of Auto cascades too, so in StackPanel you refer to, you can make that controls width Auto as well, likewise with the items it contains; if the stack panel merely contains a TextBlock (via some template or whatever), then you can also set this width to Auto and the width with set itself according to the contained text.
<Grid Background="AliceBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
...
<StackPanel Grid.Column=2
Width="Auto">
<TextBlock Width="Auto"
Text="This is TextBlock"/>
...
</StackPanel>
...
</Grid>
In this case the Text of the TextBlock sets the width of the StackPanel, which in turn sets the width of the 3rd grid column.
I hope this helps.
My WPF XAML setup is as follows (bits removed to keep this simple):
<UserControl ....>
<Grid Background="AliceBlue" Name="TopGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="12*" Name="Graph" />
<ColumnDefinition Width="5" Name="GridSplitter" />
<ColumnDefinition Width="2*" Name="ManualControlsSplit" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="AliceBlue"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<!-- A Graph and simple controls -->
</Grid>
<!-- GRID SPLITTER -->
<GridSplitter Grid.Column="1" Width="5"
HorizontalAlignment="Stretch" Name="Splitter" />
<Label Grid.Column="1" Content="⁞" Foreground="White"
Background="DarkGray"
VerticalAlignment="Center" FontSize="26" FontWeight="Bold"
IsHitTestVisible="False"/>
<!-- end GRID SPLITTER -->
<StackPanel Grid.Column="2" Grid.Row="0" Margin="5"
Name="TemperatureControls">
<!-- Load of Controls -->
</StackPanel>
</Grid>
</UserControl>
To calculate the desired width I use the following code:
// get my UserControl object
var manualControlView = userControl as HeatingController.Views.ManualControlView;
// Query the current width of THIRD column
double actualWidth = manualControlView.ManualControlsSplit.ActualWidth;
// Set up a BIG size (this has to be bigger size than the contents of the
// THIRD column would ever need)
Size size = new Size(400, manualControlView.TopGrid.ActualHeight);
// Ask WPF layout to calculate the Desired size.
manualControlView.TemperatureControls.Measure(size);
double width = manualControlView.TemperatureControls.DesiredSize.Width;
if (actualWidth <= width)
{
// Too small - do something
}
else
{
// big enough - do something else.
}
The variable 'width' now contains the value I wanted to calculate.

Grid column not collapsing to fit contents after width resize

I have a grid column that contains a treeview which can shrink to a width of 16 when a ToggleButton is pressed. The column is set to Width="Auto" which I assume is responsible for the shrinking behavior.
The problem is that to the right of the column, there is a grid splitter, so that the user can increase/decrease the width as they see fit. If the user does this, and then presses the ToggleButton, the contents of the column shrink to 16, but the column itself does not.
Does anyone know how I can ensure that the column shrinks in these scenarios? I'm guessing it has to do with the column width being changed from auto to a definite value, but I can't think of how to fix that once the contents shrink. Here's my code so far:
<Grid Name="ContentGrid" HorizontalAlignment="Stretch" Grid.Row="1" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" x:Name="TreeColumn"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" MinWidth="400" />
</Grid.ColumnDefinitions>
<UserControl DockPanel.Dock="Left" Grid.Row="0" Grid.Column="0" Padding="0" x:Name="TreeControl" >
<local:TreeViewControl x:Name="mainTreeView" Height="Auto" />
</UserControl>
<GridSplitter Grid.Row="0" Grid.Column="1" Margin="0,0,0,0"
Width="4" Background="Transparent" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
<Grid Name="LandingPageGrid" Grid.Row="0" Grid.Column="2">
EDIT: I've tried adding a DataTrigger to the column's definition, but this hasn't worked.
<ColumnDefinition Width="Auto" x:Name="TreeColumn">
<ColumnDefinition.Style>
<Style>
<Style.Triggers>
<DataTrigger {Binding Path=IsChecked, ElementName=CollapseIcon}" Value="True">
<Setter Property="ColumnDefinition.Width" Value="Auto"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
So after much debugging, many fruitless attempts, I finally resorted to setting the errant widths in the code-behind to the TreeViewControl. It's not pretty, so if someone would like to suggest a better answer, please do so, and I'll happily mark your code as the solution.
private void CollapseIcon_Checked(object sender, RoutedEventArgs e)
{
TreeControl.Width = 16;
TreeControl.HorizontalAlignment = HorizontalAlignment.Left;
((FrameworkElement) TreeControl.Parent).Width = 16;
var parentColumn = (((Grid) ((FrameworkElement) TreeControl.Parent).Parent).ColumnDefinitions)[0];
parentColumn.Width = GridLength.Auto;
}
private void CollapseIcon_Unchecked(object sender, RoutedEventArgs e)
{
TreeControl.Width = Double.NaN;
TreeControl.HorizontalAlignment = HorizontalAlignment.Stretch;
((FrameworkElement) TreeControl.Parent).Width = Double.NaN;
var parentColumn = (((Grid)((FrameworkElement)TreeControl.Parent).Parent).ColumnDefinitions)[0];
parentColumn.Width = GridLength.Auto;
}

How to align buttons horizontally and spread equally with xaml windows phone

I have an ObservableCollection which contains ViewModel which in turns defines my buttons definitions.
I've been at it for hours, reading articles after articles but to no avail. I've tried using a Listbox and this is the closest I've got to. My buttons are getting build horizontally but assuming I'm displaying 3 buttons, I want one displayed on the left, one displayed in the middle and one displayed on the right.
<ListBox Grid.Row="2" ItemsSource="{Binding Links}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<StackPanel Background="Beige" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Grid.Column="{Binding Column}"
Grid.Row="0"
Width="90"
Height="90">
<ContentControl>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Image}" Width="36" Height="36"
Margin="5" Stretch="UniformToFill"
HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Description}"
Foreground="#0F558E"
FontSize="18"
HorizontalAlignment="Center"/>
</StackPanel>
</ContentControl>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
As you can see, I set the column dynamically using a property from my ViewModel but I no longer have a grid as I couldn't get it to work, but ideally I'd like to use a grid so that I can specify in which Column to set the button.
When I use a StackPanel, it works but the buttons are right next to each other rather than being split evenly through the entire width of the screen.
I've done something similar to the above using ItemsControl and using a grid, and I can see each button getting added but they overlap each other in row 0, col 0. If I bind the row to the Column property for testing purposes, it build it correctly but my buttons are displayed on different rows which is not what I want. I want each button to be aligned horizontally.
How can I achieve this? I know I could just hard code 3 buttons and just change their icons and text and handle the relevant code by passing the relevant button as binded parameter, but ideally I'd like to build the buttons dynamically in a grid and position them using the column.
Note that the number of column would be fixed i.e. 3, as I'll never have more than this.
Thanks.
but ideally I'd like to use a grid so that I can specify in which
Column to set the button.
In any Xaml variant, why not just use that Grid, such as shown below, where the Grid is set to consumes all the horizontal space.
Then with the grid's center column to be star sized and to have the rest of the remaining space be consumed after button 1 and button 3, which auto size into their own spaces:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"/>
<Button Grid.Column="1" HorizontalAlignment="Center"/>
<Button Grid.Column="2"/>
</Grid>
If that fails, set button one's HorizontalAlignment to be Left and button three's as Right.
As an aside with the list box, it may not be stretching to the whole horizontal size of the screen. Check out my answer to a WP8 sizing issue:
ListBoxItem HorizontalContentAlignment.
I eventually found the answer to my problem in an article I found on the web.
You can check it out here: Using Grids with ItemsControl in XAML
In short, you need to subclass the itemsControl and you need overwrite the GetContainerForItemOverride method which will take care of copying the "data" of the ItemTemplate to the ContentPresenter. In this instance, the row and column, but for my requirement, it is just the Column, since my row will always be 0.
Here is core part of the code if you don't want to check the full article which resolve the problem of setting controls horizontally in a grid using ItemsControl but note the article takes care of creating rows & columns dynamically as well, which I'm not interested in for my project.
public class GridAwareItemsControl : ItemsControl
{
protected override DependencyObject GetContainerForItemOverride()
{
ContentPresenter container = (ContentPresenter)base.GetContainerForItemOverride();
if (ItemTemplate == null)
{
return container;
}
FrameworkElement content = (FrameworkElement)ItemTemplate.LoadContent();
BindingExpression rowBinding = content.GetBindingExpression(Grid.RowProperty);
BindingExpression columnBinding = content.GetBindingExpression(Grid.ColumnProperty);
if (rowBinding != null)
{
container.SetBinding(Grid.RowProperty, rowBinding.ParentBinding);
}
if (columnBinding != null)
{
container.SetBinding(Grid.ColumnProperty, columnBinding.ParentBinding);
}
return container;
}
}
The final xaml looks like this:
<controls:GridAwareItemsControl ItemsSource="{Binding Links}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Background="Pink">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Grid.Column="{Binding Column}"
Grid.Row="0"
Width="120" Height="120">
<ContentControl>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Image}" Width="36" Height="36" Margin="5"
Stretch="UniformToFill" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Description}" Foreground="#0F558E"
FontSize="18" HorizontalAlignment="Center"/>
</StackPanel>
</ContentControl>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</controls:GridAwareItemsControl>
Once I used the new control, my buttons were correctly placed inside the grid, and therefore were evenly spaced out as the grid took care of that wit the ColumnDefinitions.
If anyone knows how to achieve the same without having to create a new control and overriding the method (in other words, pure XAML), please post it as I'd be very interested to see how this can be done.
Thanks and thank you to Robert Garfoot for sharing this great code!
PS: Note that I've simplified my xaml in order to create a test sample without any style on the buttons, so these are rather large if you try based on this sample.
UPDATE:
Small typo correction but my grid column definition was defined as
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
but as #OmegaMan suggested, to be evenly spread, it should have been defined as
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
I was able to accomplish this with a stackpanel inside of a grid, avoiding columns altogether. If you set the stackepanel's HorizontalAlignment to "center", it will center itself inside the grid and grow as buttons are added, still staying centered inside of the grid. Then it's just a matter of margins to have the buttons equally spaced:
<Grid>
<StackPanel
VerticalAlignment="Stretch"
HorizontalAlignment="Center"
Orientation="Horizontal"
>
<Button HorizontalAlignment="Center" Content="Add" Width="104" Height="32" Margin="24,0"/>
<Button HorizontalAlignment="Center" Content="Edit" Width="104" Height="32" Margin="24,0"/>
<Button HorizontalAlignment="Center" Content="Remove" Width="104" Height="32" Margin="24,0"/>
</StackPanel></Grid>

Stop Gridsplitter stretching content beyond window

Given the below XAML, how do I have the gridsplitter respect the MinHeight given to the 3rd row and have the content remain inside my window?
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition MinHeight="40" />
</Grid.RowDefinitions>
<Expander Grid.Row="0" ExpandDirection="Down" VerticalAlignment="Top">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" MinHeight="100" Background="Black" />
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="LightBlue" ResizeBehavior="PreviousAndCurrent" />
</Grid>
</Expander>
<Expander Grid.Row="1" ExpandDirection="Down" VerticalAlignment="Top">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" MinHeight="100" Background="Black" />
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="LightBlue" ResizeBehavior="PreviousAndCurrent" />
</Grid>
</Expander>
<Border DockPanel.Dock="Bottom" Grid.Row="2" Background="Lime" MinHeight="30" >
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DockPanel},Path=ActualHeight,StringFormat={}{0:f0}}" />
</Border>
</Grid>
The way your code is, this cannot be done mate. This is due to how the GridSplitter works.
A few points
A GridSplitter will always work on directly adjacent rows/columns
In reality, your MinHeight IS being respected, but so is the GridSplitter's request to grow being respected, which results in the Grid growing larger than your Window
When sized to Auto, a row/column will always resize according to its content, not bigger, and not smaller
Therefore if a GridSplitter is sandwiched between two * sized rows/columns, then it would implicitly respect your MinHeight, since in reality, it would not be touching it
You have a few solutions
Add another row in the 3rd position which is * sized, and have your border on row 3 with a RowSpan of 2 (so the 3rd row is the one being really resized, and your 4th row isn't touched. Though this will also have side-effects.
Handle a mixture of DragEnter and PreviewMouseMove events on the GridSplitter, keeping track of focus, and cancelling (e.Handled = true) the event when a certain size is reached.
This is what I can think of mate, hope I've been of some help.
I created a custom grid splitter class that will not allow the grid splitter to go off the edge of a window (either the bottom or the side).
Public Class CustomGridSplitter
Inherits GridSplitter
Public Enum SplitterDirectionEnum
Horizontal
Vertical
End Enum
Public Property SplitterDirection As SplitterDirectionEnum
Public Property MinimumDistanceFromEdge As Integer
Private _originPoint As Point
Private Sub customSplitter_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles MyBase.MouseDown
_originPoint = e.GetPosition(Window.GetWindow(Me))
End Sub
Private Sub customSplitter_PreviewMouseMove(sender As Object, e As MouseEventArgs) Handles MyBase.PreviewMouseMove
If e.LeftButton = MouseButtonState.Pressed Then
Dim pwindow As Window = Window.GetWindow(Me)
Dim newPoint As Point = e.GetPosition(pwindow)
If SplitterDirection = SplitterDirectionEnum.Horizontal Then
If newPoint.Y >= _originPoint.Y Then
If newPoint.Y >= pwindow.ActualHeight - MinimumDistanceFromEdge Then
e.Handled = True
End If
Else
If newPoint.Y > pwindow.ActualHeight - (MinimumDistanceFromEdge + 2) Then
e.Handled = True
End If
End If
Else
If newPoint.X >= _originPoint.X Then
If newPoint.X >= pwindow.ActualWidth - MinimumDistanceFromEdge Then
e.Handled = True
End If
Else
If newPoint.X > pwindow.ActualWidth - (MinimumDistanceFromEdge + 2) Then
e.Handled = True
End If
End If
End If
_originPoint = newPoint
End If
End Sub
End Class
To use it in XAML:
<CustomGridSplitter SplitterDirection="Vertical" MinimumDistanceFromEdge="100" x:Name="splitterCenter" ResizeDirection="Columns" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" Width="2" Margin="2,0,2,0"/>
The custom properties to set are the "SplitterDirection" and "MinimumDistanceFromEdge". Everything works like the base grid splitter.
This uses mouse events to determine where in the window the user is dragging the splitter and handles the events if they get too close to the edge.
I found another solution to this problem, though in a much more simple case where I just had two columns inside a window that I wanted to resize.
The solution that I came up with (described in more detail here: https://stackoverflow.com/a/46924893/6481970) was to add event callbacks for when the grid was resized, when the GridSplitter moved, and when the window was resized (to handle the case where you resize the window to no longer fit the content because the grid doesn't automatically resize itself to fit the smaller window).
Here is some simplified code:
XAML:
<Grid x:Name="ResizeGrid" SizeChanged="ResizeGrid_SizeChanged">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="C0" Width="150" MinWidth="50" />
<ColumnDefinition Width="5" />
<ColumnDefinition x:Name="C2" Width="*" MinWidth="50" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="Green" />
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" DragCompleted="GridSplitter_DragCompleted" />
<Grid Grid.Column="2" Background="Red" />
</Grid>
C# Code Behind:
C0.MaxWidth = Math.Min(ResizeGrid.ActualWidth, ActualWidth) - (C2.MinWidth + 5);

How to have a click-able button in my combo-box ItemTemplate?

This is what i have so far:
<dxe:ComboBoxEdit Name="cboUserCustomReports"
Width="300" Height="Auto"
Margin="0,5,0,5"
ItemsSource="{Binding Path=UserReportProfileList,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
EditValue="{Binding Path=UserReportProfileID,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ValueMember="UserReportProfileID"
DisplayMember="ReportName"
PopupClosed="cboUserCustomReports_PopupClosed">
<dxe:ComboBoxEdit.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding ReportName, Mode=Default}"
VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
<Button Name="btnDelete"
Grid.Column="1"
Width="20" Height="20"
VerticalAlignment="Center" HorizontalAlignment="Right"
Click="btnDelete_Click">
<Button.Template>
<ControlTemplate>
<Image Source="/RMSCommon;component/Resources/Delete.ico"></Image>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</DataTemplate>
</dxe:ComboBoxEdit.ItemTemplate>
</dxe:ComboBoxEdit>
First, i want the two columns to be stand alone. The user must be able to select or delete the item.
Second, i would like to make my button in the ItemTemplate to be click-able.
What do i need to add to get this behavior?
This is what it looks like at the moment:
Click
I assume, that your button is clickable, and you want to know how to process the click event. Right?
For the click-handler, add the following code:
private void btnDelete_Click(object sender, RoutedEventArgs e) {
FrameworkElement fe = sender as FrameworkElement;
if(null == fe){
return;
}
UserReportProfile userReportProfile = fe.DataContext as UserReportProfile;
if (null == userReportProfile) {
return;
}
// Do here your deletion-operation
}
I assumed that your item-class is named UserReportProfile. Otherwise, change the declared type accordingly.
Layout
For the alignment, add the following declaration to your ComboBox:
HorizontalContentAlignment="Stretch"
This gives your DataTemplate-Grid the full width and you can layout then your items as you desire.
<dxe:ComboBoxEdit Name="cboUserCustomReports"
HorizontalContentAlignment="Stretch"
Width="300" Height="Auto"
Margin="0,5,0,5"
...>
Your question is not clear enough. But I guess you want to vertically align the text and images in your combobox. If so, then all you need to do this:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
And I think your items are already clickable!

Resources