Text trimming in the beginning of the string - wpf

I have an application in WPF and I want see in my textboxes only the end of the string.
XAML:
<Grid Height="109" Width="126">
<Grid.RowDefinitions>
<RowDefinition Height="166*" />
<RowDefinition Height="145*" />
</Grid.RowDefinitions>
<TextBlock Text="10000004" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" Width="40" Background="LightBlue"/>
<TextBlock Text="10000005" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" Width="40" Grid.Row="1" Background="LightGreen"/>
</Grid>

As you probably have seen on MSDN, you will need to create/extend with your own TextTrimmingProperty dependency property.
Look at this to extend your TextBlock so that you can create your own TextTrimmingProperty to work differently.

int Len = 4; //put this equcal to how many digits you need from last...
//path is the actual string
string endText = path.Substring(path.Length - Len, path.Length)

Related

Chinese font renders incorrectly at FontSize less than 22

For the most part, my app renders different character sets without problems. There are, however, certain Chinese characters that render fine when FontSize is 22, but horribly when it is smaller:
This was rendered by the following piece of XAML, which works for both WPF and UWP, with the same problematic results:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox x:Name="textBox" Grid.Row="0" FontSize="21" Text="〾⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻" />
<TextBox Grid.Row="1" FontSize="22" Text="{Binding Text, ElementName=textBox}" />
<TextBlock Grid.Row="2" FontSize="21" Text="{Binding Text, ElementName=textBox}" />
<TextBlock Grid.Row="3" FontSize="22" Text="{Binding Text, ElementName=textBox}" />
</Grid>
The second and fourth line shows how the string is supposed to look; the first and third show garbled versions.
The answer to this somewhat related question suggests setting FontFamily="SimSun", which does indeed cause things to be displayed correctly. My options, then, currently seem to be:
Change the font family from the default "Segoe UI" to "SimSun"
Increase the font size from 15 to 22
Neither option is appealing. Does anyone have other suggestions? Or an explanation for why those particular characters are so troublesome?
Turns out FontWeight="Light" does the trick:
Medium, Normal and SemiLight all fail. Light, SemiBold, Thin, Bold, ExtraBold, Black, ExtraBlack and ExtraLight all solve the problem.
If anyone knowledgeable about this area has something to contribute, I'm still interested, as I have no idea what causes this behavior.

How to bind Font Size to variable Grid Size

I have a grid with 2 columns and 2 rows. A single character (Unicode U+2699) was placed inside the bottom right grid field. It looks like this:
I´d like the character to automatically adjust its font size to fit the grid field it has been placed in (in this case it should be bound to the height of the second grid row, but since in some cases it could be unclear if the height or the width of the grid is smaller, it would be also nice to know how to bind to the lowest of those 2 values).
My implementation so far is something like the following (I simplified it a bit for this example):
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition x:Name="heightToBind" Height="40"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="14*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button FontSize="{Binding ElementName=heightToBind, Path=Height.Value, Mode=OneWay}" Content="⚙" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" />
</Grid>
The problem here is, that it only works if the height is a fixed value inside the RowDefinitions. I want it to work with the following definition:
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition x:Name="heightToBind" Height="*"/>
</Grid.RowDefinitions>
As a bonus question I´d also be interested why it might be that the character is placed too low so it is getting cut off at the bottom (I tried VerticalAlignment="Center" for the button but with no effect).
You can try using a ViewBox as the button's content:
<Button Grid.Row="1" Grid.Column="1">
<Button.Content>
<Viewbox StretchDirection="Both" HorizontalAlignment="Stretch">
<TextBlock Text="⚙" />
</Viewbox>
</Button.Content>
</Button>
A ViewBox can stretch and scale his child to fill all the available space...
You could try binding to the ActualHeight instead of the Height:
<Button FontSize="{Binding ElementName=heightToBind, Path=ActualHeight.Value, Mode=OneWay}"
Content="⚙" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" />
This should work.
The * on the grid definition means take the available space as the height so it's only determined when the page layout has been prepared for layout. If the height is either unset or changed then the real height is returned in the ActualHeight property.

How to dynamically add buttons and Silverlight player in a Grid?

I'm basically trying to create the following set of code dynamically/programmatically but I'm unsure of how to do it.
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<smf:SMFPlayer x:Name="player" Grid.Row="0" AutoPlay="False">
<smf:SMFPlayer.Playlist>
<media:PlaylistItem
DeliveryMethod="AdaptiveStreaming"
MediaSource="http://video3.smoothhd.com.edgesuite.net/ondemand/Big%20Buck%20Bunny%20Adaptive.ism/Manifest"/>
<media:PlaylistItem
DeliveryMethod="AdaptiveStreaming"
SelectedCaptionStreamName="textstream_eng"
MediaSource="http://streams.smooth.vertigo.com/elephantsdream/Elephants_Dream_1024-h264-st-aac.ism/manifest"/>
</smf:SMFPlayer.Playlist>
</smf:SMFPlayer>
<StackPanel Grid.Row="1" Orientation="Horizontal" Background="Transparent">
<Button x:Name="test1" Height="30" Width="70" Content="Test 1"/>
<Button x:Name="test2" Height="30" Width="70" Content="Test 2"/>
</StackPanel>
</Grid>
Here's how it looks statically:
First of all, you must give a name to your StackPanel like this;
<StackPanel x:Name="spBottom" Grid.Row="1" Orientation="Horizontal" Background="Transparent">
<Button x:Name="test1" Height="30" Width="70" Content="Test 1"/>
<Button x:Name="test2" Height="30" Width="70" Content="Test 2"/>
</StackPanel>
And then, you must add the following lines in code-behind;
For iLoop As Integer = 0 to 4
Dim btn As New Button With {.Content = "Button" & iLoop}
spBottom.Children.Add(btn)
Next iLoop
I hope this will be help to you!
The controls without an xmlns (XML namespace) prefix can be created in your code-behind without adding any usings. For example, in C# you can recreate the StackPanel from your XAML using the following code:
StackPanel panel = new StackPanel() { Orientation = Orientation.Horizontal, Background = null };
panel.SetValue(Grid.RowProperty, 2);
LayoutRoot.Children.Add(panel);
The elements with an xmlns prefix, anything with a colon, such as <smf: require knowledge of the namespace in the codebehind. The associated namespaces are defined in the first element and look like xmlns:smf="PathToTheNamespace". This namespace is often refrenced in a codebehind file in C# by adding a using PathToTheNamespace statement at the top.

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);

GridSplitter with min constraints

I want a Grid layout with two rows and splitter between them. Rows should have a minimum height of 80 pixels.
This code works great:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="80" />
<RowDefinition Height="5" />
<RowDefinition Height="*" MinHeight="80" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
<GridSplitter Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Red" />
<TextBlock Grid.Row="2" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
</Grid>
But I want top row to have an Auto height until user manually change it using the splitter. So I changed the code to this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="80" />
<RowDefinition Height="5" />
<RowDefinition Height="*" MinHeight="80" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
<GridSplitter Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Red" />
<TextBlock Grid.Row="2" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
</Grid>
And there is a problem. Splitter still satisfies row constraints, but it begins to increase top row's height infinitely if I drag splitter too low. This results in bottom row to be completely below window's bottom border.
I have done some Reflector on GridSplitter code and see that it uses different logic if rows has Auto or Star height.
Any suggestions how can I "fix" it?
I've run into this problem a few times myself. It appears as though the GridSplitter doesn't play well with Auto. That said, I have found a potential workaround.
You are able to specify the value of a GridLength object using "star coefficients". This acts as a multiplier for the length in question.
In your example, if you take the row you want to remain as star, and set the the star coefficient to a really large number, the row will take up all available space (forcing the other row to become its min-height). While this isn't the same behavior as "auto" (the height of the first row is not determined by its contents height), it might get you closer.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="80" />
<RowDefinition Height="5" />
<RowDefinition Height="10000*" MinHeight="80" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
<GridSplitter Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Red" />
<TextBlock Grid.Row="2" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
</Grid>
I have developed a workaround for this problem. Point is to set MaxHeight for the top row while we are dragging splitter. Here the code:
public class FixedGridSplitter : GridSplitter
{
private Grid grid;
private RowDefinition definition1;
private double savedMaxLength;
#region static
static FixedGridSplitter()
{
new GridSplitter();
EventManager.RegisterClassHandler(typeof(FixedGridSplitter), Thumb.DragCompletedEvent, new DragCompletedEventHandler(FixedGridSplitter.OnDragCompleted));
EventManager.RegisterClassHandler(typeof(FixedGridSplitter), Thumb.DragStartedEvent, new DragStartedEventHandler(FixedGridSplitter.OnDragStarted));
}
private static void OnDragStarted(object sender, DragStartedEventArgs e)
{
FixedGridSplitter splitter = (FixedGridSplitter)sender;
splitter.OnDragStarted(e);
}
private static void OnDragCompleted(object sender, DragCompletedEventArgs e)
{
FixedGridSplitter splitter = (FixedGridSplitter)sender;
splitter.OnDragCompleted(e);
}
#endregion
private void OnDragStarted(DragStartedEventArgs sender)
{
grid = Parent as Grid;
if (grid == null)
return;
int splitterIndex = (int)GetValue(Grid.RowProperty);
definition1 = grid.RowDefinitions[splitterIndex - 1];
RowDefinition definition2 = grid.RowDefinitions[splitterIndex + 1];
savedMaxLength = definition1.MaxHeight;
double maxHeight = definition1.ActualHeight + definition2.ActualHeight - definition2.MinHeight;
definition1.MaxHeight = maxHeight;
}
private void OnDragCompleted(DragCompletedEventArgs sender)
{
definition1.MaxHeight = savedMaxLength;
grid = null;
definition1 = null;
}
}
Then just replace GridSplitter with FixedGridSplitter.
Note: this code is not general - it doesn't support columns and assume that bottom row has MinHeight specified.

Resources