WPF How to make a textbox slideable - c#

So I have a TextBox with fixed height and width.
I want to have a slider or something else on the TextBox to make the missing text visible.
By missing text I mean the text that is longer than the width of the TextBox.
It doesn't have to be a slider just something that allows me to see the cut text.
I tried:
<TextBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto" />
but it does nothing for me.

Test this:
<TextBox ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Width="300"
Height="200"
TextWrapping="Wrap"
AcceptsReturn="True"
FontSize="22pt"
Text="Some loooooooooong text. Some loooooooooong text. Some long text. Some long text. Some long text. Some long text. Some long text. Some long text. "
/>
The key here is setting TextWrapping="Wrap". Also, if you want to be able to make a new line with the ENTER key, set AcceptsReturn="True".
If the scroll bar doesn't show for you, maybe your Width and Height isn't as "fixed" as you thought. The size of ScrollViewer needs to be limited somehow. Make sure that your TextBox is properly positioned in its container. For example, you may temporarily set its BorderThickness="5" and see if it isn't clipped anywhere (this may be the case if you have any HorizontalAlignment / VerticalAlignment other than Stretch).
EDIT:
As I mentioned, for scrolling a TextBox, all depends on the value of TextWrapping property.
For a TextBox, scrolling horizontally out-of-the-box only only makes sense if the text isn't wrapped (meaning there's only one very long line):
<TextBox ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
Width="300"
Height="200"
TextWrapping="NoWrap"
FontSize="22pt"
Text="Some loooooooooong text. Some loooooooooong text. Some long text. Some long text. Some long text. Some long text. Some long text. Some long text. "
/>
If you want to have more than one line of text to scroll it horizontally, you have to wrap a TextBox in a ScrollViewer explicitly and set the sizes correctly to limit the ScrollViewer's content:
<ScrollViewer
Width="200"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<TextBox Name="txb1"
Width="500"
VerticalAlignment="Stretch"
TextWrapping="Wrap"
AcceptsReturn="True"
FontSize="22pt"
Text="Some loooooooooong text. Some loooooooooong text. Some long text. Some long text. Some long text. Some long text. Some long text. Some long text. "
/>
</ScrollViewer>
I guess if you want to "auto-adjust" the width of the TextBox somehow, you'd need to calculate it as text changes. A way to do it off the top of my head (there are probably better solutions):
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
txb1.Width = txb1.Text.Split('\n').Max(a =>a.Length) * approximatedCharWidth;
}
where as approximatedCharWidth I used:
approximatedCharWidth = MeasureString(txb1, "lext").Width/4.0;
private Size MeasureString(TextBox textBlock, string candidate)
{
var formattedText = new FormattedText(
candidate,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch),
textBlock.FontSize,
Brushes.Black,
new NumberSubstitution(),
1);
return new Size(formattedText.Width, formattedText.Height);
}

If you just have a plain string with no line feeds then this can only work for horizontal OR vertical scrolling.
If you can scroll horizontally then you're telling the text it can be as wide as it likes. Which means you will get one line.
If you instead wanted it to go across multiple lines then you either need linefeeds in the text or avoid any horizontal scrolling.
Seeing as you don't seem to want vertical scrolling I tried this:
<TextBox ScrollViewer.HorizontalScrollBarVisibility="Auto"
Text="ABCDEFGIJKLMNOPQRSTUVWXYZ ABCDEFGIJKLMNOPQRSTUVWXYZ ABCDEFGIJKLMNOPQRSTUVWXYZ ABCDEFGIJKLMNOPQRSTUVWXYZ"
Height="36"
Width="200"
/>
Which gives:
Which seems to be what you're after.

Related

WPF ListView items to be displayed with individual height

I'm currently trying to display a Dictionary (which is held in a Dictionary itself).
I started at first using a UniformGrid as ItemsPanelTemplate, but realized pretty fast, the items to display can have individual heights.
I've got so far, that I can display all content using the UniformGrid, but can't seem to get it working using a Grid or StackPanel as ItemsPanelTemplate.
The code below is working fine with the downside that each Operation-block is given the same height though their height can be variable.
After given some thought I came to the conclusion that a StackPanel would be best to use, as the Operations would be shown bleow each other taking the height they needed. But when I tried, I relaized they take only a fraction of the ListView's height.
Worth to mention:
The Operation-UserControl in itself does evaluate its height and build its layout accordingly. So it doesn't take the space needed to display all content, but displays the content which does fit in the available space.
So how can I achieve that the ListViewItems (=operations) take the ListView's full height?
EDIT: clarification
if the described behaviour isn't possible with any above mentioned control, but any other could provide the needed funtionality, let me know...
EDIT2: some examples
Total available space: 500
No Scrollbar.
Sidenote: there is no maxItemLimit, but it's highly unlikely that the ItemCount would exceed 10.
Given 1 item: (needed space to display all content 300)
This single item would take 300.
Given 2 items: (these would need 150 and 200 of space)
Both items would be displayed in there full size: 150, 200.
(Presumably only working with a StackPanel.)
Given 10 items:
Those 10 would be squeezed equally or relative to full-desired size in the 500 (so 50 per item).
Both behaviours would be fine.
UniformGrid vs StackPanel
<UserControl x:Name="vDay" x:Class="RefServiceClient.Day"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:RefServiceClient"
mc:Ignorable="d"
d:DesignHeight="60" d:DesignWidth="120"
MinHeight="40">
<Grid x:Name="gridDay"
Width="{Binding ActualWidth, ElementName=vDay, Mode=OneWay}"
Height="{Binding ActualHeight, ElementName=vDay, Mode=OneWay}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="DayHeader" Grid.Row="0">
<!--containing header info: several textboxes, which are irrelevant for the question-->
<TextBox x:Name="dayShortname"
Grid.Row="0" Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
Margin="1"
Text="{Binding Path=Value.DayShortname}"/>
</Grid>
<ListView x:Name="operations" Grid.Row="1" Background="Aqua"
ItemsSource="{Binding Path=Value.OperationList}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<local:Operation Background="Crimson" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch"/>
<!--<local:Operation Background="Crimson" />-->
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<!--<Grid Grid.IsSharedSizeScope="True"/>-->
<!--<StackPanel/>-->
<!--<VirtualizingStackPanel Orientation="Vertical"/>-->
<UniformGrid Columns="1"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
You could create a custom panel that arranges your item according to your rules. Then you just have to design your items in a way that they display nicely for whatever size they are allowed to take.
A rough sketch of the panel could look as follows:
public class SqueezeStackPanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
var desiredHeight = 0.0;
foreach (UIElement child in InternalChildren)
{
child.Measure(availableSize);
desiredHeight += child.DesiredSize.Height;
}
if (availableSize.Height < desiredHeight)
{
// we will never go out of bounds
return availableSize;
}
return new Size(availableSize.Width, desiredHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
// measure desired heights of children in case of unconstrained height
var size = MeasureOverride(new Size(finalSize.Width, double.PositiveInfinity));
var startHeight = 0.0;
var squeezeFactor = 1.0;
// adjust the desired item height to the available height
if (finalSize.Height < size.Height)
{
squeezeFactor = finalSize.Height / size.Height;
}
foreach (UIElement child in InternalChildren)
{
var allowedHeight = child.DesiredSize.Height * squeezeFactor;
var area = new Rect(new Point(0, startHeight), new Size(finalSize.Width, allowedHeight));
child.Arrange(area);
startHeight += allowedHeight;
}
return new Size(finalSize.Width, startHeight);
}
}
This panel can be used in the ItemsPanelTemplate of your ListView with disabled scrollbars.
It depends. I'll give you an options. First. Implement, let say, Boolean AttachedProperty, marking whether this particular instance should be of certain size. In case 0/1 is not sufficient, declare appropriate enumeration. Second. Extend existing StackPanel, override appropriate protected members. At least, MeasureOverride/ArrangeOverride. There you can read the value of corresponding attached property and decide how big or small it has to be. Does it sound like a solution? In case it does, I can provide some examples.

Textblock dyanmic placement WPF

I have a text data. I would like to place the data dynamically to a certain point [Absolute point, it may be (0,0) (10,10) or (100,100)].
Here the problem comes. My requirement is text block's center point should be the base point. How to make it and place it directly on the canvas? [Which means align the absolute point and center point of the textblock]. Because in winforms we can set the alignment property and place it directly. Please check this question .It describes how to move the textblock after placing it on the canvas since actual width can be found.
I have two specific questions
Is it possible to do it in WPF
Is there anyway to find the actual width before placement, in case I can do it as in the other question
Update (Further explanation)::::: This is my Data Template to place the text, I can use TranslateTransform to translate the text as soon as the text is placed on the canvas by using MoveToPoint function mention below.
The real problem come, how to pass the ActualWidth to MoveToPoint inside datatemplate,
XAML
<DataTemplate DataType="{x:Type local:Text}">
<TextBlock Text="{Binding Description}"
FontSize= "{Binding Thickness}"
RenderTransformOrigin="0.5,0.5"
Foreground="#FFF63AFF"
FontWeight="Bold" >
<TextBlock.RenderTransform>
<TransformGroup>
<TranslateTransform X= "{Binding StartPoint.X}" Y= "{Binding StartPoint.Y}" />
<RotateTransform Angle= "{Binding Angle}" />
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
</DataTemplate>
C#
void MoveToPoint(UIElement sender, Point point)
{
Canvas.SetLeft(sender, point.X - sender.RenderTransformOrigin.X * sender.ActualWidth);
Canvas.SetTop(sender, point.Y - sender.RenderTransformOrigin.Y * sender.ActualHeight);
}

Why Border disappear when setting big Width?

I resize the border according to timeline. Why when the Width becomes very big the Border disappear. For example for Width=100000 the Border is visible but for Width=200000 the Border disappears.
<StackPanel>
<Border BorderThickness="0,0,0,1" BorderBrush="Black" Height="100" Width="1000000">
</Border>
</StackPanel>
It turns out, that there are some limitations on the Border setting BorderThickness property.
Unfortunately, I can not say exactly how they look, I tried to find them using ILSpy (you can try to look for them am).
One limitation I can say: if the value of one coordinate over 125,000 the line of Border disappears. Here is my list of examples:
Thickness Width
--------- ---------
0,0,0,1 125 001
0,0,0,2 251 000
0,0,0,3 375 001
0,0,0,4 501 000
In all these cases, the line is not drawn.
As an alternative, you can increase the value for Thickness each time (not varinat), or using a Line / Separator, for them, there should be no restrictions. You need to change the value of Margin, depending on conditions or setting the Visibility for him.
Example with Separator:
<Separator Name="HighSignal"
Width="1000000"
Background="Black"
Height="2"
Visibility="Collapsed"
Margin="0,100,0,0" />
<Separator Name="LowSignal"
Width="1000000"
Background="Black"
Height="2"
Visibility="Visible"
Margin="0,0,0,0" />
Note: For Separator you may create a Style, because for him base type a Control (for Line -Shape).

the display word gets cut off 1 position

In the following code, there is a column that gets displayed called
'All day event' for some reason here the 't' is getting cut off. can you point me if there is any value here too large that would cause this?
<dxe:CheckEdit x:Name="chkAllDay" Margin="0,6,202,4" Grid.Column="2"
Content="{Binding Source={StaticResource SchedulerControlStringIdConverter},
ConverterParameter=Form_AllDayEvent,
Converter={StaticResource SchedulerControlStringIdConverter}}"
EditValue="{Binding Controller.AllDay}" IsReadOnly="{Binding ReadOnly}"
IsEnabled="True" HorizontalAlignment="Right" Width="81" />
Your CheckEdit has its Width fixed to 81 pixels and also fixed Margins*. From your code snippet I cannot guess how the whole layout looks, but be sure to re-calculate those values. Maybe you've just accidentally got 5px off and the remaining area is too small for the text?
*) I said about Margin, but I've mistaken them with Padding. Margin is not relevant here, they it's the outer spacing' and does not count to the Width. OTOH, the Padding would. I think here's one good explanation

Rendering text in WPF so that it perfectly fits in a given rectangle

I need to display words on a WPF Canvas in such a way that they perfectly fit in pre-defined boxes.
One box typically contains a single line of text, from one letter to a few words.
The text inside a box must be as large as possible, i.e: touching all borders of the box (except maybe where it would cause too much text distortion due to abnormal box witdh/height ratio).
I could not find a good way to calculate the appropriate font height, scaling and offset, based on the text content.
A first solution where the original text width/height ratio can't be changed would already be very nice !
I'd like to use TextBlock elements, but anything else that works should be ok.
As the answer by Robery Levy said, you can use a Viewbox to achieve this. The text itself won't stretch however so you'll still have some "margin" on zero or more sides depending on your text (as you noticed). To work around this you can create a custom control which builds a Geometry from a FormattedText and then draw this with DrawGeometry in OnRender. You'll notice how the quality of the text improves with a larger FontSize. A very small text (e.g. FontSize="10") won't look very sharp in a large Viewbox so you'll have to experiment a bit
Some sample Xaml
<Canvas Background="Black">
<Viewbox Canvas.Left="10" Canvas.Top="10"
Stretch="Fill" Width="200" Height="50">
<Border Background="Red">
<local:StretchText Text="Text" Foreground="Green" FontSize="100"/>
</Border>
</Viewbox>
<Viewbox Canvas.Left="230" Canvas.Top="10"
Stretch="Fill" Width="200" Height="50">
<Border Background="Red">
<local:StretchText Text="B" Foreground="Green" FontSize="500"/>
</Border>
</Viewbox>
</Canvas>
StretchText.cs
public class StretchText : Control
{
protected override void OnRender(DrawingContext drawingContext)
{
FormattedText formattedText = new FormattedText(
Text,
CultureInfo.GetCultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface(FontFamily, FontStyle, FontWeight, FontStretches.Normal),
FontSize,
Foreground);
Geometry textGeometry = formattedText.BuildGeometry(new Point(0, 0));
this.MinWidth = textGeometry.Bounds.Width;
this.MinHeight = textGeometry.Bounds.Height;
TranslateTransform translateTransform = new TranslateTransform();
translateTransform.X = -textGeometry.Bounds.Left;
translateTransform.Y = -textGeometry.Bounds.Top;
drawingContext.PushTransform(translateTransform);
drawingContext.DrawGeometry(Foreground, new Pen(Foreground, 1.0), textGeometry);
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text",
typeof(string),
typeof(StretchText),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender));
}
Put the TextBlock inside a Viewbox: http://msdn.microsoft.com/en-us/library/system.windows.controls.viewbox.aspx
You could check out Charles Petzold's article "Render Text on a Path with WPF". Unfortunately I can't refresh my knowledges about subject at present moment due to something goes wrong with MSDN site, but he described how to scale text within path.

Categories