Textbox will not stretch to fill viewbox - wpf

I want the font size of my labels and textboxes in my LOB form to grow and shrink with window resize or resolution change. To achieve this I've placed my labels and textboxes within viewboxes.
The labels and custom radio buttons behave as I expect, but the textboxes will not stretch horizontally to fill the viewbox (sorry can't post image because of rep). The textboxes will horizontally fill the viewbox if you type into them.
Here is an example of the code which I am working with:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.186*"/>
<ColumnDefinition Width="0.814*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.127*"/>
<RowDefinition Height="0.873*"/>
</Grid.RowDefinitions>
<Viewbox Margin="0,0,0.917,0">
<Label Content="First name:"/>
</Viewbox>
<Viewbox Grid.Column="1">
<TextBox TextWrapping="Wrap"/>
</Viewbox>
</Grid>
I've tried placing grid, stackpanel and dockpanel (with LastChildFill="True") within the viewbox and then placing the textboxes within these layout controls but this didn't work either. Is there anyway to get the textboxes to horizontally fill the parent viewbox?
This problem is similar to this: WPF TextBox won't fill in StackPanel but this problem is with stackpanels, not viewboxes.

I think what you want is not easily possible. ViewBox tells its children that they have inifite space, in the measure pass of the layouting. After that, the display is fit into the viewbox. Now a textbox, told to have infinite space, obviously can't stretch.
I have 2 solutions, which i think are not what you want, but might be helpful anyway.
The first:
<Viewbox Grid.Column="1" Grid.Row="0" Stretch="Uniform" >
<Grid Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Viewbox}}, Path=ActualWidth}">
<TextBox TextWrapping="Wrap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
</Viewbox>
this will stretch you textbox infact, but disable the behaviour expected from the viewbox. Why? We told the ViewBox to keep the aspect ratio, and set the textbox to fill the whole width of the viewbox, which keeps the size of the textbox.
The second:
would be to add a height to the grid, taken from the label, modified with the scaletransform of its viewbox.
This one i haven't tried, but it would involve a value converter.
In conclusion: There is no easy way to achieve what you want, because of the way the viewbox layouts its children. If you just want to stretch horizontally, my first solution works fine, if you want to scale. My guess is you have to do it yourself.

If what you want doesn't work/isn't easy then fake it:
<TextBox GotFocus="TextBox_GotFocus" HorizontalAlignment="Stretch" BorderThickness="0" Grid.Column="1"/>
<Viewbox HorizontalAlignment="Left" Stretch="Uniform" Grid.Column="1">
<TextBox x:Name="NameTextBox" Width="50" TextWrapping="Wrap" Text="Text" BorderThickness="0"/>
</Viewbox>
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
NameTextBox.Focus();
NameTextBox.SelectionStart = NameTextBox.Text.Length;
}
Basically what happens is another TextBox is behind the Viewbox and when the behind TextBox gets focus, it switches focus to the Viewbox's TextBox. This will produce some odd resizing since you have your grid setup with relative sizes. You will need to play around with your grid column/width sizes until you get the effect you desire.

Just use a converter
Set the FontSize like this:
FontSize="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight ,Converter={StaticResource heightconverter}, ConverterParameter=3}"
public class ProcentualHeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is string p)
{
if (value is double v)
{
var result = double.TryParse(p, out double param);
if (result)
{
return v / param;
}
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

ViewBox FontSize Issue

I am attempting to use a ViewBox to adjust the font size for different controls and not the control size itself. The problem is that whenever I put a control like a TextBox or ComboBox it'll resize the font properly, but the control will not stretch. I know this has to do with the 'infinite' amount of space the ViewBox can use.
I tried a different approach to where I had TextBlock inside of a ViewBox and bound another controls fontsize to that TextBlock.
<Grid Width=100>
<ViewBox>
<TextBlock Name=aTextBlock />
</ViewBox>
</Grid>
...
<Grid>
<TextBlock FontSize={Binding ElementName=aTextBlock, Path=FontSize}/>
</Grid>
This approach did not work. It would be nice if I could bind to the ActualFontSize but that does not exist.
One last approach was to use a ViewModelBase that bound the FontSize of aTextBlock to a variable and have the other textblock to that fontsize.
private double _currentFontSize;
public double GlobalFontSize
{
get { return _currentFontSize; }
set
{
_currentFontSize = value;
MessageBox.Show(_currentFontSize.ToString());
RaisePropertyChanged();
}
}
NOTE: I tried using int, double, and FontSize types for this class.
<Grid Width=100>
<ViewBox>
<TextBlock Name=aTextBlock
FontSize={Binding GlobalFontSize,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay} />
</ViewBox>
</Grid>
...
<Grid>
<TextBlock FontSize={Binding GlobalFontSize}>
</Grid>
And this did not work either. I'm starting to feel like the ViewBox is more trouble than it's worth. If I am doing any of this incorrectly or if anyone has any recommendations I would appreciate it very much.

Prevent a TextBox from horizontal expanding in WPF

I have the following style defined in my App.xaml
<Style x:Key="textBoxMultiline" TargetType="{x:Type TextBox}" >
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="MinHeight" Value="50" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
And throughout the solution we're using it on every text box that needs a brief text.
<TextBox x:Name="textBoxDescription" Grid.Row="2" Grid.Column="1" Style="{DynamicResource textBoxMultiline}" />
Everything works great, but then the client complains about some fields were corped on older monitors with lower resolutions, so I placed a ScrollViewer on one of the higher visual tree nodes to prevent the corping.
<ScrollViewer Height="Auto" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
...
</ScrollViewer>
Strangely, the TextBoxes with the above style start expanding to the right instead of wrapping the text.
Is there a way to prevent this without removing the ScrollViewer?
If you don't want to hard code the width then you can go for element binding the width of the parent item.
Here I am binding TextBox MaxWidth with ScrollViewer actual width. You also have to make sure that the ColumnDefinition width should be set to "*" not to "Auto". If you set it to Auto it will neglect the ScrollViewer width and keep on expanding the width of ScrollViewer and TextBox. I think you fall in this case...
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto" Name="scv">
<TextBox Height="30" TextWrapping="Wrap" MaxWidth="{Binding ElementName=scv, Path=ActualWidth}"></TextBox>
</ScrollViewer>
</Grid>
You must define a MaxWidth for the TextBox, otherwise there's no limit because the ScrollViewer.
The solution provided from #bathineni helped me solve my problem. Here is what worked for me:
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Width="30" Height="23" Margin="10,5" Content="..."/>
<ScrollViewer Grid.Column="1" HorizontalScrollBarVisibility="Disabled" verticalScrollBarVisibility="Disabled" Name="scv">
<TextBox Height="25" Text="Insert here long text" MaxWidth="{Binding ElementName=scv, Path=ActualWidth}" HorizontalAlignment="Stretch" />
</ScrollViewer>
</Grid>
I tried the aforementioned examples and they didn't work so, I solved the problem myself. There are two ways of solving this issue:
The first solution is implemented in XAML using data bindings. I advice you not to bind the control by itself. The XAML solution is implemented by binding a control with the desired ActualWidth and ActualHeight proprieties to the textbox MaxHeight and MaxWidth proprieties.
<TextBlock x:Name="PasswordText" Margin="0,0,0,20" FontFamily="Bahnschrift SemiBold Condensed" Text="PASSWORD" FontSize="20">
<TextBox x:Name="PasswordTextBox" MaxWidth="{Binding ElementName=PasswordText, Path=ActualWidth}" MaxHeight="{Binding ElementName=PasswordText, Path=ActualHeight}">
The next solution is implemented by generating a Loaded event in XAML, creating it in the C# code and then setting within the Loaded event the MaxWidth and MaxHeight proprieties of the textbox as the textbox ActualWidth and ActualHeight proprieties.
// It doesn't have a problem like in XAML if you pass the textbox its own
// ActualWidth and ActualHeight to the MaxWidth and MaxHeight proprieties.
private void Log_In_Page_Loaded(object sender, RoutedEventArgs e)
{
UsernameTextBox.MaxHeight = UsernameTextBox.ActualHeight;
UsernameTextBox.MaxWidth = UsernameTextBox.ActualWidth;
}
Choose the one that suits your design better, but I think, in my opinion, that this is the most effective, simple, stable and effective way of solving this problem.
Works for me. If you want scrollbars to appear in the textbox, you may add
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
to the TextBox
You must set MaxWidth of the Container Control
<Grid x:Name="RootGrid" Margin="6,6,8,8" Width="500" MaxWidth="500">
<ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Name="contentGroup" Header="Content" Grid.Row="0">
<TextBox Name="content"/>
</GroupBox>
</Grid>
</ScrollViewer>
I ran into this problem when I needed my TextBox to stretch along with its auto-sized Grid column when it got resized, meaning MaxWidth wouldn't work, but still needed to prevent the TextBox from stretching along with its contents.
What I ended up doing was linking this event handler to SizeChanged:
private void TextBox_SizeChanged(object sender, SizeChangedEventArgs e) {
TextBox textBox = (TextBox)sender;
if (textBox.CanUndo && e.NewSize.Width > e.PreviousSize.Width) {
textBox.Width = e.PreviousSize.Width;
}
}
Writing text to a textbox is something that the user can Undo, whereas other actions that can cause resizing (initial drawing of the element, stretching of parent container, etc) aren't Undoable from within the textbox. Thus, by checking CanUndo we can determine whether SizeChanged was triggered by writing text or something else.
The e.NewSize.Width > e.PreviousSize.Width check is necessary because without it the SizeChanged event will infinitely be called from within itself, because to revert the stretching we need to change the size back to the original, which would itself trigger the event.
It's a little hacky but I haven't run into any issues yet.
I am not sure why but I could not get the ScrollViewer solution to work. I needed to have a TextBox with a fixed initial width to implement a numeric up/down control - in this control the TextBox was shrinking and growing independent of the input which looks very annoying if the UI changes as you type.
So, I found the below solution using 2 textboxes to work for me. The first textbox is the textbox displayed for the user to type their input and the 2nd textbox is initialized through a dependency property (DisplayLength) and the converter shown further below.
Binding the MaxWidth property of the 1st TextBox to the Width property of the 2nd TextBox fixes the size such that users can type what they want but the displayed width of the textbox will not change even if there is more UI space available.
<TextBox x:Name="PART_TextBox"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value}"
Margin="0,0,1,0"
TextAlignment="Right"
AcceptsReturn="False"
SpellCheck.IsEnabled="False"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MaxWidth="{Binding ElementName=TBMeasure, Path=ActualWidth}"
/>
<!-- Hidden measuring textbox ensures reservation of enough UI space
according to DisplayLength dependency property
-->
<TextBox x:Name="TBMeasure"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DisplayLength, Converter={StaticResource ByteToPlaceHolderStringConverter}}"
Margin="0,0,1,0"
TextAlignment="Right"
AcceptsReturn="False"
SpellCheck.IsEnabled="False"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="Hidden"/>
// Converter
[ValueConversion(typeof(byte), typeof(string))]
public sealed class ByteToPlaceHolderStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((value is byte) == false)
return Binding.DoNothing;
byte byteVal = (byte)value;
string retString = string.Empty;
for (int i = 0; i < byteVal; i++)
retString = retString + "X";
return retString;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

Independently resizing children in Grid or Stackpanel

I have an image I want to display in a control in a touch application. As well as the image there is a textbox to display underneath it, as a tab, docked at the bottom left of the image. The tab has a lower width than the image. The user can resize, move and rotate the image, but I'd like the textbox tab to stay the same size and in the same relative position to the image.
I've tried using both a StackPanel and a Grid, but both times the textbox/tab is scaled up as well as the image.
Are either a Grid or StackPanel the way to go, and if so how can I enforce the size of the textbox/tab (that is, the second child) as the size of the first child changes?
Thanks!
In respones to Lars:
<Grid Name="mygrid" Background="Red" Width="320" Height="300">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="255"/>
<RowDefinition MaxHeight="40" />
</Grid.RowDefinitions>
<!--Ist child-->
<Canvas Name="maincanvas" Background="DarkKhaki" Width="300" Height="180" Grid.Row="0">
<!--<Image goes in here>-->
</Canvas>
<!--2nd child-->
<DockPanel Name="dockpanel" Grid.Row="1" Background="DarkKhaki" MaxWidth="200" HorizontalAlignment="Left">
<TextBlock Name="textblock" TextWrapping="Wrap" >
some text here
</TextBlock>
</DockPanel>
</Grid>
What I want to do is allow the user to drag and resize the Image(1st child), while maintaining the size and relative position of the TextBlock (2nd child). So the effect is of a tab anchored to the bottom left of the image that is fixed as the image can dynamically resize.
I tried to add images to make this clearer but as a new user I can't, sorry!
It's ironic that you used a DockPanel in your XAML replacement for an image in your post. :) I think that's really what you want here -- a DockPanel with the top portion that scales its contents, and a lower portion that doesn't.
The key is to add the image container to the DockPanel last, so that it automatically is given the rest of the screen real estate. And set its Dock property to "Top".
e.g. (I haven't tested this, BTW. I am just thinking that this will work a little better than what I posted earlier)
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<!-- put LeftMarginConverter here -->
</Page.Resources>
<DockPanel>
<TabControl DockPanel.Dock="Bottom" Margin="{Binding ImageX, Converter={StaticResource LeftMarginConverter}}">
<TabItem Header="Some text here" />
</TabControl>
<Canvas DockPanel.Dock="Top">
<Image Canvas.Left="{Binding ImageX}">
<!-- image goes here -->
</Image>
</Canvas>
</DockPanel>
</Page>
So the idea here is that your image position in the canvas is databound to a property in code-behind. Likewise, the TabControl's Margin property is databound to this same property. But in order to use it, you'll need to write an IValueConverter in code behind, and this converter will take the value of ImageX and return a new Thickness object that uses ImageX as its left margin.
e.g.
public class LeftMarginConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int left_margin = object as int;
return new Thickness( left_margin,0,0,0);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// we aren't ever going to need to convert back for this formatter!
return null;
}
#endregion
}
Will here again: LayoutTransform was a much, much simpler solution to this. I put the main image and the label into a stackpanel. Then I applied the scale transform to the image as a LayoutTransform rather than a RenderTransform, and applied the Translate and Rotate transforms to the stackpanel. Problem solved :-)

Wrapping panels in WPF

I have the following layout in my window:
Grid with two columns
GridSplitter which resizes grid columns
Second grid column is filled with StackPanel
StackPanel is oriented vertically and has 2 children: TextBlock and a WrapPanel
WrapPanel has two Grids as children
First Grid child contains one Image
Second Grid contains a StackPanel with 3 TextBlocks oriented vertically.
The XAML code looks like this:
<Window>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Left" />
<StackPanel Grid.Column="1" Margin="5,0,0,0" Orientation="Vertical"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="Now here's a silly poem for you." />
<WrapPanel>
<Grid Name="GridForImage">
<Image Width="200" Height="200" Source="Image.jpg" />
</Grid>
<Grid Name="GridForText">
<StackPanel Orientation="Vertical">
<TextBlock TextWrapping="WrapWithOverflow" Text="Roses are red." />
<TextBlock TextWrapping="WrapWithOverflow" Text="Violets are blue." />
<TextBlock TextWrapping="WrapWithOverflow" Text="You belong in a zoo." />
</StackPanel>
</Grid>
</WrapPanel>
</StackPanel>
</Grid>
</Window>
Once the window opens, the second column is wide enough to allow grids GridForImage and GirdForText to be placed next to each other horizontally. If I shrink the width of the second column using the grid splitter, the GridForText grid gets placed underneath the GridForImage at one point, which is quite expected.
Here's what I would like to achieve:
I want GridForText to shrink its width to a certain size and to remain positioned to the right of the GridForImage, as I move the grid splitter to the right side of the window. Then, when the width shrinks to a certain value, say 200px, it should get placed underneath the GridForImage, i.e. WrapPanel should do its magic. Right now, the GridForText doesn't resize at all, it just gets placed underneath when it's current width becomes too large for the width of the WrapPanel.
When the GridForText does get placed underneath the GridForImage, I want GridForImage to fill the entire width of the WrapPanel's width.
Is all this possible and what should I do? Thank you all.
You're essentially trying to use two distinct layout modes so you just need to set up the two distinct states in your layout and then add bindings or triggers to switch between them at the point when you want to switch modes (i.e. width = 200). Using a Grid is the most flexible and gives you a lot more control over the relative sizes but requires more settings and would work best in a ControlTemplate or DataTemplate where you can use Triggers to set a bunch of things at once based on a condition.
Here's a more compact example using UniformGrid with some Bindings and a converter. I removed the fixed sizing on the Image - try Stretch="Fill" if you care more about filling width than aspect ratio. I also changed one StackPanel to a DockPanel to maintain vertical stretching for its children and added a Background to one of the TextBlocks just to show how much Width it's really getting:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Left" />
<DockPanel Grid.Column="1" Margin="5,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="Now here's a silly poem for you." DockPanel.Dock="Top"/>
<UniformGrid Rows="{Binding RelativeSource={RelativeSource Self}, Path=ActualWidth, Converter={x:Static local:LayoutModeConverter.Row}, ConverterParameter=200}"
Columns="{Binding RelativeSource={RelativeSource Self}, Path=ActualWidth, Converter={x:Static local:LayoutModeConverter.Column}, ConverterParameter=200}">
<Image Source="Image.jpg" />
<StackPanel Orientation="Vertical">
<TextBlock TextWrapping="WrapWithOverflow" Text="Roses are red." Background="Red" />
<TextBlock TextWrapping="WrapWithOverflow" Text="Violets are blue." />
<TextBlock TextWrapping="WrapWithOverflow" Text="You belong in a zoo." />
</StackPanel>
</UniformGrid>
</DockPanel>
</Grid>
And the converter:
public class LayoutModeConverter : IValueConverter
{
public static readonly LayoutModeConverter Row = new LayoutModeConverter { RowMode = true };
public static readonly LayoutModeConverter Column = new LayoutModeConverter { RowMode = false };
public bool RowMode { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double width = System.Convert.ToDouble(value);
double targetWidth = System.Convert.ToDouble(parameter);
if (RowMode)
return width > targetWidth ? 1 : 2;
else
return width > targetWidth ? 2 : 1;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

WPF font scaling

I have a WPF application where the user interface should be scaled so that it should become larger if the windows is made larger. In one of the dialogs, I need to present a list of items to a user and the user should click one of them. The list will contain from 1 to around 15-20 items. I want the font size for each individual item to be as big as the font size for the other items in the list, but at the same time I want the font size to increase if the window is made larger.
At the moment, my test code looks like below.
<Window x:Class="WpfApplication4.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WpfApplication4"
Title="Window1" Height="480" Width="640">
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30*" MinHeight="30"/>
<RowDefinition Height="30*" MinHeight="30"/>
<RowDefinition Height="30*" MinHeight="30"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" MaxHeight="100"><Viewbox><TextBlock>T</TextBlock></Viewbox></Button>
<Button Grid.Row="1" MaxHeight="100"><Viewbox><TextBlock>Test</TextBlock></Viewbox></Button>
<Button Grid.Row="2" MaxHeight="100"><Viewbox><TextBlock>Test Longer String</TextBlock></Viewbox></Button>
</Grid>
</ScrollViewer>
</Window>
If the application is started and the Window is made wide, everything looks OK. If the window width is decreased, the font size of the text Test Longer String is made smaller, but the font size for T and Test remains the same. I do understand why this happens - the viewbox will scale the contents to its maximum size. What I want to know is what method I should use to solve this problem.
I don't want to give the controls specific font sizes because some people will run this on low-resolution screens such as 640x480 and others will use larger widescreens.
EDIT:
I've tried to modify my code to the following:
<ScrollViewer>
<Viewbox>
<ItemsControl>
<Button>Test 2</Button>
<Button>Test 3</Button>
<Button>Test 4 afdsfdsa fds afdsaf</Button>
<Button>Test 5</Button>
<Button>Test 5</Button>
<Button>Test 5</Button>
<Button>Test 5</Button>
<Button>Test 5</Button>
</ItemsControl>
</Viewbox>
</ScrollViewer>
But with the size of the button borders are increased as well, so on large screens, the button borders become a centimeter wide.
Try this: Render your text items like you would do it any other time:
TextBlocks contained in an 'ItemsControl'?
ListBox?
Place the whole shebang into a ViewBox and set it to scale appropriate to your needs. Look for Horizontal, Vertical or combined scaling properties.
This workaround might help:
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30*" MinHeight="30"/>
<RowDefinition Height="30*" MinHeight="30"/>
<RowDefinition Height="30*" MinHeight="30"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" MaxHeight="100">
<Viewbox>
<TextBlock Width="{Binding ElementName=tbLonger,Path=ActualWidth}">T</TextBlock>
</Viewbox>
</Button>
<Button Grid.Row="1" MaxHeight="100">
<Viewbox>
<TextBlock Width="{Binding ElementName=tbLonger,Path=ActualWidth}">Test</TextBlock>
</Viewbox>
</Button>
<Button Grid.Row="2" MaxHeight="100">
<Viewbox>
<TextBlock Name="tbLonger">Test Longer String</TextBlock>
</Viewbox>
</Button>
</Grid>
</ScrollViewer>
The key is to set all textblocks the same width. In this case they all follow the longest textblock through binding.
I had to make an resolution independent application and made it with this answer here:
tips on developing resolution independent application
with that your application is scaling (or zooming), depending on the resolution.
If you only want the FontSize to change then bind it to your MainWindow ActualWidth (say) and use a converter to scale:
<Window.FontSize>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" ConverterParameter="20">
<Binding.Converter>
<local:ScaleConverter />
</Binding.Converter>
</Binding>
</Window.FontSize>
The converter parameter is how you set the font size at some arbitrary width size (1000 pixels, say), which your converter will then also take into account:
internal class ScaleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Double.Parse(parameter.ToString()) * (double)value / 1000;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
The simplest way is probably to construct a decorator to do the job. You might call it "VerticalStretchDecorator" or something like that.
Here is how it would be used:
<UniformGrid Rows="3" MaxHeight="300">
<Button>
<my:VerticalStretchDecorator>
<TextBlock>T</TextBlock>
</my:VerticalStretchDecorator>
</Button>
<Button>
<my:VerticalStretchDecorator>
<TextBlock>Test</TextBlock>
</my:VerticalStretchDecorator>
</Button>
<Button>
<my:VerticalStretchDecorator>
<TextBlock>Test Longer String</TextBlock>
</my:VerticalStretchDecorator>
</Button>
</UniformGrid>
I used UniformGrid instead of Grid, but it would work the same way with Grid.
It would be implemented something like this:
public class VerticalStretchDecorator : Decorator
{
protected override Size MeasureOverride(Size constraint)
{
var desired = base.Measure(constraint);
if(desired.Height > constraint.Height || desired.Height==0)
LayoutTransform = null;
else
{
var scale = constraint.Height / desired.Height;
LayoutTransform = new ScaleTransform(scale, scale); // Stretch in both directions
}
}
}

Resources