I am making a UserControl called VolumeScrollBar to control the volume a video player I am working on. Now the problem I am having is when I slide my SwitchPath it moves noticeable slower than my mouse. How can I fix this ?
note: everything is working but the speed.
Also, I am not using the built in sliding bar because I do not like how it looks. So I designed my own Volume scroll bar in Adobe Illustrator.
here is my XAML code:
<Canvas x:Name="VolumeScrollBarCanvas" Height="21" Width="160" >
<Path x:Name="Path_7" Width="15" Height="13" Canvas.Left="0.777" Canvas.Top="3.556" Stretch="Fill" Fill="#FFFFFFFF" Data="F1 M 67.6183,648C 67.5503,648 67.4809,648.019 67.4156,648.061L 62.6556,651.173L 61.3916,652L 55.9996,652C 54.5276,652 53.3329,653.193 53.3329,654.667C 53.3329,656.14 54.5276,657.333 55.9996,657.333L 61.3916,657.333L 62.6556,658.16L 67.4156,661.272C 67.4809,661.315 67.5503,661.333 67.6183,661.333C 67.8169,661.333 67.9996,661.169 67.9996,660.937L 67.9996,654.667L 67.9996,648.395C 67.9996,648.164 67.8169,648 67.6183,648 Z M 66.6663,650.144L 66.6663,654.667L 66.6663,659.189L 63.3863,657.044L 62.1209,656.217L 61.7889,656L 61.3916,656L 55.9996,656C 55.2649,656 54.6663,655.401 54.6663,654.667C 54.6663,653.932 55.2649,653.333 55.9996,653.333L 61.3916,653.333L 61.7889,653.333L 62.1209,653.116L 63.3849,652.289L 66.6663,650.144 Z " HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Path x:Name="WhiteBarPath" Width="81" Height="7" Canvas.Left="46.111" Canvas.Top="6.223" Stretch="Fill" Fill="#FFFFFFFF" Data="F1 M 176.667,657.333L 102,657.333C 100.167,657.333 98.6667,655.833 98.6667,654L 98.6667,654C 98.6667,652.167 100.167,650.667 102,650.667L 176.667,650.667C 178.5,650.667 180,652.167 180,654L 180,654C 180,655.833 178.5,657.333 176.667,657.333 Z " HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Path x:Name="Path_8" Width="13" Height="20" Canvas.Left="24.151" Canvas.Top="0.639" Stretch="Fill" Fill="#FFFFFFFF" Data="F1 M 78.6667,645.333C 77.9893,645.333 77.3347,645.451 76.7067,645.648C 81.5307,646.025 85.3333,650.212 85.3333,655.333C 85.3333,660.455 81.5293,664.641 76.7067,665.019C 77.3347,665.216 77.9893,665.333 78.6667,665.333C 83.0853,665.333 89.3333,660.856 89.3333,655.333C 89.3333,649.811 83.0853,645.333 78.6667,645.333 Z " HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Path x:Name="Path_9" Width="9.26" Height="15" Canvas.Left="19.168" Canvas.Top="3.556" Stretch="Fill" Fill="#FFFFFFFF" Data="F1 M 73.1609,648C 72.6636,648 72.1849,648.085 71.7236,648.231C 75.2609,648.508 78.0503,651.579 78.0503,655.333C 78.0503,659.088 75.2609,662.159 71.7236,662.436C 72.1849,662.581 72.6636,662.667 73.1609,662.667C 76.4009,662.667 80.9836,659.384 80.9836,655.333C 80.9836,651.283 76.4009,648 73.1609,648 Z " HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Path x:Name="Path_10" Width="4" Height="5" Canvas.Left="18.11" Canvas.Top="8.266" Stretch="Fill" Fill="#FFFFFFFF" Data="F1 M 72.4039,652.71C 72.2399,652.71 70.9452,652.756 70.6665,652.81L 70.6665,657.311C 71.0052,657.364 72.3412,657.306 72.4039,657.306C 73.6732,657.306 74.7012,656.276 74.7012,655.007C 74.7012,653.739 73.6732,652.71 72.4039,652.71 Z " HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Path x:Name="ColorBarPath" Width="81" Height="7" Canvas.Left="46.111" Canvas.Top="6.223" Stretch="Fill" Fill="#FF6557FF" Data="F1 M 176.667,657.333L 102,657.333C 100.167,657.333 98.6667,655.833 98.6667,654L 98.6667,654C 98.6667,652.167 100.167,650.667 102,650.667L 176.667,650.667C 178.5,650.667 180,652.167 180,654L 180,654C 180,655.833 178.5,657.333 176.667,657.333 Z " HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Ellipse x:Name="SwitchPath" MouseDown="SwitchPath_MouseDown" MouseMove="SwitchPath_MouseMove" Mouse.MouseUp="SwitchPath_MouseUp" Width="19" Height="19" Canvas.Left="115.444" Canvas.Top="0.889" Stretch="Fill" Fill="#FFFFFFFF" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Path x:Name="Path_7_Copy" Width="15" Height="13" Canvas.Left="0.777" Canvas.Top="3.556" Stretch="Fill" Fill="#FFFFFFFF" Data="F1 M 67.6183,648C 67.5503,648 67.4809,648.019 67.4156,648.061L 62.6556,651.173L 61.3916,652L 55.9996,652C 54.5276,652 53.3329,653.193 53.3329,654.667C 53.3329,656.14 54.5276,657.333 55.9996,657.333L 61.3916,657.333L 62.6556,658.16L 67.4156,661.272C 67.4809,661.315 67.5503,661.333 67.6183,661.333C 67.8169,661.333 67.9996,661.169 67.9996,660.937L 67.9996,654.667L 67.9996,648.395C 67.9996,648.164 67.8169,648 67.6183,648 Z M 66.6663,650.144L 66.6663,654.667L 66.6663,659.189L 63.3863,657.044L 62.1209,656.217L 61.7889,656L 61.3916,656L 55.9996,656C 55.2649,656 54.6663,655.401 54.6663,654.667C 54.6663,653.932 55.2649,653.333 55.9996,653.333L 61.3916,653.333L 61.7889,653.333L 62.1209,653.116L 63.3849,652.289L 66.6663,650.144 Z " HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Path x:Name="Path_6" Width="13" Height="12" Stretch="Fill" Fill="#FFFFFFFF" Data="F1 M 61.5899,656.667L 56.0005,656.667C 54.8965,656.667 54.0005,655.769 54.0005,654.667C 54.0005,653.564 54.8965,652.667 56.0005,652.667L 61.5899,652.667L 67.3339,648.912L 67.3339,660.421L 61.5899,656.667 Z " HorizontalAlignment="Left" VerticalAlignment="Top" Canvas.Left="1.777" Canvas.Top="4.056"/>
<TextBlock x:Name="Displaytxb" Height="18" Canvas.Left="136.069" TextWrapping="Wrap" Text="100" Canvas.Top="1.917" Width="23" HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="White"/>
</Canvas>
and here is my code behind:
private void ChangeTheVolume(double newVolume)
{
if (newVolume < 0 || newVolume > 100) return;
newVolume = Math.Round(newVolume);
double tempVolume = (ValumeMaxHigh * newVolume);
ColorBarPath.Width = tempVolume;
double pointX = (LocationMaxHigh * newVolume) + 46.11;
Canvas.SetLeft(SwitchPath, pointX); // set the left position of rectangle to mouse X
Displaytxb.Text = Convert.ToString(newVolume);
VolumeOut = Volume;
}
private void SwitchPath_MouseDown(object sender, MouseButtonEventArgs e)
{
Cursor = Cursors.Hand;
_handToolMove = true;
SwitchPath.CaptureMouse();
_startPoint = e.GetPosition(VolumeScrollBarCanvas);
double mouseCurrentLocation = Canvas.GetLeft(SwitchPath);
_mouseDownForHandToolOffSetX = _startPoint.X - mouseCurrentLocation;
}
private void SwitchPath_MouseMove(object sender, MouseEventArgs e)
{
if (_handToolMove)
{
Point v = e.GetPosition(VolumeScrollBarCanvas);
Volume = 0.6933 * v.X + 1.24946035807;
}
}
private void SwitchPath_MouseUp(object sender, MouseButtonEventArgs e)
{
if (_handToolMove)
{
Point v = e.GetPosition(VolumeScrollBarCanvas);
Volume = 0.6933 * v.X + 1.25;
SwitchPath.ReleaseMouseCapture();
_handToolMove = false;
Cursor = Cursors.Hand;
}
}
}
public VolumeScrollBar()
{
RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
InitializeComponent();
}
The issue here isn't the speed of the render but just in your geometry calculations. The volume is not scaling 1:1 with the mouse movement. (That is, in terms of y=mx+b, m needs to be 1).
So, this line in SwitchPath_MouseMove -
Volume = 0.6933 * v.X + 1.24946035807;
Would be better written as
Volume = (v.X - 46.11) / ValumeMaxHigh;
That markedly improves things after trying it out with your code.
In English, what we want is the position of the mouse X relative to the left edge of the slider, normalized by the total width of the track. So that means mouse X minus the left offset divided by the total available width - which you hard code in VolumeMaxHigh (which seems to be the actual width / 100, so this guarantees volume will always be out of 100).
If you had a much more complex graphic, you would want to use a RenderTransform during the mouse move, and set Canvas.Left only on the mouse up, but this is simple enough where performance is not the issue.
Anyway, if you were to consider redoing this I might suggest writing a general purpose slider of your own that is not dependent on any particular dimensions but rather learns its dimensions at runtime through ActualWidth and the like. The source code for WPF is online, specifically for Slider, which will show you how that would be done. Reproducing existing controls in your own coding style is one of the best ways to learn WPF IMO so I'd encourage it as a learning exercise.
You can almost certainly re-template Slider to get what you want too, and if all you want to do is solve the problem at hand and go home that's perfectly fine. But since you put in all this effort already it's worth learning how to do right, as sooner or later you'll come up with a genuine need to write a complex custom control.
Also whether you decide to re-template Slider or make your own, studying the default template for Slider will help too.
It might be interesting to give an idea how how I re template the slider for our brass sliders.
I'll miss out the complicated stuff for the brass teardrop since it's not relevent here.
This is a bit of a dirty approach but I don't need a bunch of functionality in a regular slider.
A slider has a track - the container for everything. In that go two repeatbuttons ( or buttons or whatever you wanted actually ) which kind of look like the track but clicking them moves your pointer.
Then there is the pointer itself which is a thumb. A thumb in wpf is something intended to be dragged.
<ControlTemplate x:Key="BrassSlider" TargetType="Slider">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<Rectangle Grid.Row="1" Fill="Gray"
Stroke="Transparent"
StrokeThickness="1"
Height="13" VerticalAlignment="Center"
Margin="0,0,4,0">
<Rectangle.Effect>
<DropShadowEffect ShadowDepth="2"/>
</Rectangle.Effect>
</Rectangle>
<Rectangle Grid.Row="1"
Stroke="Sienna"
StrokeThickness="1"
Height="14" VerticalAlignment="Center"
Margin="0,0,4,0"
Fill="White"
>
</Rectangle>
<Track Grid.Row="1" x:Name="PART_Track"
>
<Track.DecreaseRepeatButton>
<RepeatButton Style="{StaticResource SliderDecreaseButtonPlain}" Command="Slider.DecreaseLarge" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Style="{StaticResource SliderIncreaseButtonPlain}" Command="Slider.IncreaseLarge" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource SliderThumb}" Grid.ZIndex="30" />
</Track.Thumb>
</Track>
</Grid>
</ControlTemplate>
Thumb, but without any teardrop complicated gubbins.
<Style x:Key="SliderThumb" TargetType="Thumb">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Control Template="{StaticResource TearDropPointer}" Height="40" Width="20" >
<Control.Effect>
<DropShadowEffect />
</Control.Effect>
</Control>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And one of the buttons.
<Style x:Key="SliderDecreaseButtonPlain" TargetType="RepeatButton">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RepeatButton">
<Border SnapsToDevicePixels="True"
Background="Transparent"
BorderThickness="0" Height="14"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You could easily make those repeatbuttons any shape you like defined by a geometry. Like you have.
Similarly, you can easily make the thumb an ellipse or whatever you fancy.
One other thing is worth mentioning.
When you see a named part in any reference template like that PART_Track above then that is likely used in the code for the control. If you miss any named parts out then there's a fair chance your control will not behave as you expect.
This slow response can happen also with a regular Slider. If this was the case I would recommend using the "Delay" to the binding of the Slider.
In your case we should do something similar : all mouse events should just keep the mouse point somewhere with no logic and no rendering. Parallelly , create a timer event to occur every 10 milliseconds (or so.. ) , and make the changes there. This way you can improve the system responsiveness.
The code behind may look as following:
Point _lastPoint;
Point _lastPointHandled;
public MainWindow()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(10);
timer.Tick += Timer_Tick; ;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
if (_lastPoint != _lastPointHandled)
{
// do your things
_lastPointHandled = _lastPoint;
}
}
private void SwitchPath_MouseMove(object sender, MouseEventArgs e)
{
_lastPoint = e.GetPosition(VolumeScrollBarCanvas);
}
private void SwitchPath_MouseUp(object sender, MouseButtonEventArgs e)
{
_lastPoint = e.GetPosition(VolumeScrollBarCanvas);
}
I have a path inside a viewboxand canvas as such:
<Viewbox x:Name="MyViewBox" Grid.Column="1" Grid.Row="1">
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="tick"
Width="146.768" Height="146.768" UseLayoutRounding="True"
Canvas.Left="0" Canvas.Top="0">
<Path x:Name="Path" Canvas.Left="4.12044" Canvas.Top="4.04301" Stretch="Fill" Fill="#FFF80000" Data="F1 M 4.28074,121.084L 4.15843,120.962C 4.40034,118.924 6.48998,117.557 7.9421,116.108C 10.772,113.284 13.6019,110.46 16.4318,107.637C 24.1129,99.9719 31.794,92.3074 39.4752,84.6428C 41.9008,82.2224 44.3264,79.802 46.752,77.3816C 47.1563,76.9782 47.5606,76.5748 47.9648,76.1714C 48.3691,75.768 48.7734,75.3646 49.1776,74.9613C 49.5819,74.5579 50.143,74.2658 50.3904,73.7511C 50.5336,73.4532 50.4055,73.0526 50.2299,72.7726C 49.9265,72.2889 49.4216,71.9658 49.0175,71.5624C 48.6134,71.159 48.2092,70.7556 47.8051,70.3522C 46.5927,69.142 45.3803,67.9318 44.1679,66.7216C 40.1265,62.6877 36.0852,58.6537 32.0438,54.6197C 25.9818,48.5687 19.9197,42.5177 13.8577,36.4668C 11.837,34.4498 9.81633,32.4328 7.79565,30.4158C 6.58325,29.2056 5.06703,28.2374 4.15843,26.7852C 4.04955,26.6112 4.20685,26.3775 4.23105,26.1737C 4.25526,25.9698 4.18553,25.73 4.30367,25.5621C 4.62703,25.1027 5.10008,24.7694 5.49829,24.3731C 5.89649,23.9767 6.2947,23.5803 6.6929,23.184C 8.28572,21.5985 9.87855,20.013 11.4714,18.4276C 14.657,15.2567 17.8427,12.0857 21.0283,8.91483C 22.6211,7.32936 23.8951,5.34012 25.8068,4.15845C 26.5968,3.67004 27.5839,4.85898 28.24,5.51649C 28.6434,5.92078 29.0468,6.32503 29.4502,6.72931C 29.8536,7.13358 30.257,7.53784 30.6604,7.94211C 33.8876,11.1763 37.1148,14.4104 40.342,17.6446C 47.6031,24.9215 54.8643,32.1983 62.1255,39.4752C 64.5459,41.9008 66.9662,44.3264 69.3866,46.752C 69.79,47.1563 70.1934,47.5606 70.5968,47.9648C 71.0002,48.3691 71.4036,48.7734 71.807,49.1776C 72.2104,49.5819 72.5025,50.143 73.0172,50.3904C 73.3151,50.5337 73.7157,50.4056 73.9957,50.2299C 74.4794,49.9265 74.8025,49.4216 75.2059,49.0175C 75.6093,48.6134 76.0127,48.2092 76.4161,47.8051C 76.8195,47.401 77.2229,46.9968 77.6263,46.5927C 80.8534,43.3596 84.0806,40.1265 87.3078,36.8934C 94.569,29.619 101.83,22.3446 109.091,15.0701C 111.512,12.6453 113.932,10.2205 116.352,7.79567C 117.563,6.58325 118.531,5.06705 119.983,4.15845C 120.763,3.67059 121.746,4.84642 122.395,5.49829C 122.792,5.89648 123.188,6.29471 123.584,6.6929C 123.981,7.09113 124.377,7.48932 124.773,7.88753C 127.152,10.2768 129.53,12.666 131.908,15.0552C 134.286,17.4445 136.664,19.8337 139.043,22.2229C 139.439,22.6211 139.835,23.0193 140.232,23.4175C 140.628,23.8157 141.024,24.2139 141.421,24.6121C 142.04,25.2343 142.971,26.3126 142.465,27.0298C 141.477,28.4297 140.039,29.4502 138.826,30.6604C 136.401,33.0808 133.975,35.5012 131.549,37.9216C 123.868,45.5862 116.187,53.2507 108.506,60.9153C 105.676,63.7391 102.846,66.5628 100.016,69.3866C 99.612,69.79 99.2077,70.1934 98.8034,70.5968C 98.3992,71.0002 97.9949,71.4036 97.5906,71.807C 97.1864,72.2104 96.6253,72.5025 96.3778,73.0172C 96.2346,73.3151 96.3627,73.7157 96.5384,73.9957C 96.8418,74.4794 97.3466,74.8025 97.7508,75.2059C 98.1549,75.6093 98.559,76.0127 98.9632,76.4161C 99.3673,76.8195 99.7714,77.2229 100.176,77.6263C 103.409,80.8534 106.642,84.0806 109.875,87.3078C 117.149,94.569 124.424,101.83 131.698,109.091C 134.123,111.512 136.548,113.932 138.973,116.352C 140.185,117.563 141.701,118.531 142.61,119.983C 143.098,120.763 141.922,121.746 141.27,122.395C 140.872,122.792 140.474,123.188 140.075,123.584C 139.677,123.981 139.279,124.377 138.881,124.773C 136.492,127.152 134.102,129.53 131.713,131.908C 129.324,134.286 126.935,136.664 124.545,139.043C 124.147,139.439 123.749,139.835 123.351,140.232C 122.953,140.628 122.554,141.024 122.156,141.421C 121.534,142.04 120.456,142.971 119.738,142.465C 118.339,141.477 117.318,140.039 116.108,138.826C 113.687,136.401 111.267,133.975 108.847,131.549C 101.182,123.868 93.5176,116.187 85.853,108.506C 83.0292,105.676 80.2054,102.846 77.3816,100.016C 76.9782,99.612 76.5748,99.2077 76.1714,98.8035C 75.768,98.3992 75.3646,97.9949 74.9612,97.5906C 74.5579,97.1864 74.2658,96.6253 73.7511,96.3778C 73.4532,96.2346 73.0526,96.3627 72.7726,96.5384C 72.2889,96.8418 71.9658,97.3466 71.5624,97.7508C 71.159,98.1549 70.7556,98.559 70.3522,98.9632C 68.3352,100.984 66.3182,103.005 64.3012,105.025C 58.2503,111.087 52.1993,117.149 46.1483,123.211C 41.7109,127.657 37.2736,132.102 32.8362,136.548C 31.626,137.76 30.4158,138.973 29.2056,140.185C 28.8022,140.589 28.3988,140.993 27.9954,141.397C 27.592,141.802 27.2693,142.307 26.7852,142.61C 26.6112,142.719 26.3775,142.561 26.1737,142.537C 25.9698,142.513 25.7301,142.583 25.5621,142.465C 25.105,142.143 24.7739,141.673 24.3798,141.277C 23.9857,140.881 23.5916,140.485 23.1975,140.089C 21.6211,138.505 20.0447,136.921 18.4683,135.338C 13.7391,130.586 9.00994,125.835 4.28074,121.084 Z "/>
</Canvas>
</Viewbox>
Up top I have had to define the width and height of the path. I was just wondering, is there a way I can not do that and simply allow silverlight to determine the width and height of the path from the canvas? Or perhaps define an explicit width/height like say 100,100 and force the inner path to draw to that. Currently the viewbox scales the path down to fit inside its parent grid and itw ould be ideal if the canvas could set the paths width and height such that it would fit.
I guess this sin't that important since I can always just keep defining width and height but I figure if I can make the xaml simpler I should try..
Ricks idea to use grids instead has worked quite well for the xaml above, however in this case:
<Viewbox Visibility="Visible" x:Name="tickbox" Grid.Column="1" Grid.Row="1">
<Grid>
<Path x:Name="Path" Canvas.Left="1.61674" Canvas.Top="423.576" Stretch="Fill" Fill="#FFF8F8F8" Data="F1 M 2.24249,423.576C 7.49307,425.327 9.74263,431.733 13.154,436.091C 15.8873,439.583 19.0756,442.697 21.9145,446.103C 32.7198,459.07 43.6481,471.933 54.4535,484.9C 75.4173,510.056 95.8553,535.67 115.503,561.867C 124.758,574.207 133.338,587.124 140.729,600.664C 143.154,605.107 144.567,610.072 147.143,614.43L 2.24249,615.056L 1.61674,614.43L 2.24249,423.576 Z "/>
<Path x:Name="Path_0" Canvas.Left="1.12545" Canvas.Top="0.352791" Stretch="Fill" Fill="#FF00B800" Data="F1 M 108.62,340.977C 110.289,341.095 111.957,341.212 113.626,341.329C 115.295,341.447 117.329,340.632 118.632,341.681C 120.441,343.139 120.731,345.827 121.78,347.9C 122.83,349.973 123.576,352.229 124.929,354.118C 128.431,359.01 131.833,363.984 134.941,369.136C 135.843,370.632 137.378,371.639 138.597,372.891C 139.816,374.142 141.325,375.166 142.254,376.645C 149.841,388.724 160.409,399.022 171.821,407.581C 178.232,412.389 185.453,416.769 193.305,418.375C 194.834,418.688 196.364,419.001 197.894,419.313C 199.423,419.626 200.958,420.592 202.482,420.252C 208.089,419.002 209.103,410.82 211.735,405.715C 217.912,393.732 223.858,381.619 230.524,369.901C 250.245,335.235 270.6,300.927 291.386,266.889C 316.703,225.431 344.166,185.23 373.312,146.369C 401.52,108.759 429.274,69.7278 465.102,39.288C 476.756,29.3865 488.046,18.5793 501.591,11.4812C 521.775,0.903687 546.274,0 569.058,0.438171C 578.918,0.627808 588.778,0.817383 598.639,1.00702C 607.812,1.18341 618.298,2.96027 624.863,9.36926C 629.283,13.6833 627.618,21.5565 627.366,27.7272C 627.132,33.4841 629.23,39.8399 626.662,44.9979C 622.944,52.4679 613.913,55.7723 607.342,60.9154C 593.594,71.6764 582.225,85.1784 570.041,97.6826C 542.594,125.851 517.912,156.713 494.473,188.295C 483.079,203.646 470.964,218.612 461.504,235.226C 460.778,236.499 459.444,237.312 458.414,238.355C 457.384,239.397 456.101,240.24 455.324,241.483C 406.922,318.927 357.946,396.555 319.232,479.268C 305.617,508.355 291.001,537.079 279.821,567.186C 276.698,575.597 273.356,583.97 269.164,591.903C 268.672,592.834 268.877,593.989 268.734,595.032C 268.59,596.075 268.649,597.166 268.303,598.161C 266.868,602.294 264.733,606.157 263.206,610.258C 262.611,611.858 262.881,614.173 261.42,615.056C 259.68,616.107 257.354,615.034 255.321,615.023C 253.288,615.012 251.255,615.001 249.222,614.99C 247.189,614.979 245.156,614.968 243.123,614.957C 228.892,614.88 214.661,614.803 200.43,614.726C 188.232,614.661 176.034,614.595 163.836,614.529C 161.803,614.518 159.77,614.507 157.737,614.496C 155.704,614.485 153.671,614.474 151.638,614.463C 149.605,614.452 147.267,615.501 145.539,614.43C 143.726,613.306 143.512,610.676 142.498,608.798C 141.485,606.921 140.471,605.044 139.458,603.167C 135.869,596.52 131.694,590.207 127.744,583.768C 114.001,561.363 97.8589,540.467 81.4389,519.942C 64.5882,498.878 47.396,478.089 30.1275,457.367C 23.1174,448.955 15.9782,440.651 8.85197,432.337C 7.06942,430.257 4.67798,428.761 2.86824,426.705C 2.43244,426.21 2.45108,425.454 2.24249,424.828L 1.61674,424.202C 1.83836,420.031 0,415.187 2.2816,411.687C 3.04422,410.517 4.77156,410.423 6.01655,409.79C 7.26153,409.158 8.64849,408.75 9.75149,407.894C 16.3236,402.792 22.8529,397.608 29.7755,392.993C 30.4704,392.53 31.5835,393.339 32.2785,392.876C 34.2191,391.582 35.3438,389.281 37.2845,387.987C 40.4037,385.907 44.1772,384.943 47.2965,382.864C 56.7196,376.581 65.4063,369.239 74.8294,362.957C 75.5244,362.494 76.6375,363.303 77.3324,362.84C 79.2731,361.546 80.3978,359.245 82.3384,357.951C 85.4577,355.871 89.2312,354.907 92.3504,352.828C 94.2911,351.534 95.4158,349.233 97.3564,347.939C 101.029,345.491 106.136,344.626 108.62,340.977 Z "/>
<Path x:Name="Path_1" Canvas.Left="1.61674" Canvas.Top="0.569702" Stretch="Fill" Fill="#FFF8F8F8" Data="F1 M 2.24249,0.569702L 538.51,1.19543C 525.297,4.70508 511.299,7.2359 499.713,14.4926C 462.987,37.4965 433.692,71.506 407.063,105.696C 387.819,130.404 368.141,154.847 350.589,180.786C 346.221,187.242 342.077,193.854 337.527,200.184C 336.703,201.33 335.624,202.27 334.672,203.313C 333.72,204.355 332.524,205.219 331.817,206.441C 321.519,224.255 308.804,240.594 298.457,258.379C 286.183,279.474 273.153,300.121 260.438,320.953C 247.317,342.451 234.983,364.434 223.132,386.657C 218.774,394.83 214.969,403.289 210.643,411.479C 209.909,412.869 209.174,414.26 208.44,415.65C 207.706,417.041 207.387,418.749 206.237,419.822C 202.371,423.428 195.548,420.45 190.385,419.313C 181.181,417.288 172.796,411.822 165.563,405.782C 155.591,397.454 146.73,387.554 139.673,376.645C 138.739,375.201 137.313,374.142 136.133,372.891C 134.954,371.639 133.502,370.597 132.594,369.136C 128.968,363.305 125.303,357.497 121.761,351.615C 119.882,348.495 119.458,343.791 116.168,342.229C 112.062,340.28 107.359,344.973 103.614,347.548C 94.2328,353.997 84.286,359.591 74.8294,365.929C 50.6338,382.147 26.4381,398.364 2.24249,414.581L 1.61674,414.19L 2.24249,0.569702 Z "/>
<Path x:Name="Path_2" Canvas.Left="260.677" Canvas.Top="40.6177" Stretch="Fill" Fill="#FFF8F8F8" Data="F1 M 626.467,40.6177C 630.002,64.1845 627.798,88.2671 627.75,112.098C 627.667,153.493 627.584,194.889 627.501,236.285C 627.248,362.542 626.994,488.799 626.741,615.056L 261.303,615.056L 260.677,614.43C 265.518,594.615 274.343,575.972 282.154,557.129C 300.25,513.473 321.398,471.097 343.276,429.208C 390.306,339.164 443.671,251.911 504.798,170.774C 527.125,141.137 551.453,112.832 578.025,86.9329C 588.006,77.2045 596.754,66.0243 607.968,57.7476C 614.73,52.757 622.557,48.0565 626.467,40.6177 Z "/>
<Path x:Name="Path_3" Canvas.Left="603.588" Canvas.Top="0.569702" Stretch="Fill" Fill="#FFF8F8F8" Data="F1 M 626.741,0.569702L 627.366,14.9619L 626.741,15.5877C 626.115,14.9619 625.361,14.4423 624.863,13.7104C 624.211,12.7509 624.17,11.3899 623.338,10.5817C 618.307,5.69745 611.097,3.15491 604.214,1.82117L 603.588,1.19543L 626.741,0.569702 Z "/>
</Grid>
</Viewbox>
the path is displayed at a fraction of the size it actually is :-/
The Path does have a Width and Height. If you are happy with that size, you can just remove the Canvas entirely and let the Viewbox scale the Path directly.
<Viewbox x:Name="MyViewBox" Grid.Column="1" Grid.Row="1">
<Path .../>
</Viewbox>
Alternatively if want some space around the Path you can use a more flexible layout element than a Canvas like a Grid:
<Viewbox x:Name="MyViewBox" Grid.Column="1" Grid.Row="1">
<Grid Margin="4">
<Path .../>
</Grid>
</Viewbox>
The Grid will auto-size to the Path (unlike the Canvas) and then you can add your margin, and then the Viewbox will start its scaling.
Is it possible to display the text in a TextBlock vertically so that all letters are stacked upon each other (not rotated with LayoutTransform)?
Nobody has yet mentioned the obvious and trivial way to stack the letters of an arbitrary string vertically (without rotating them) using pure XAML:
<ItemsControl
ItemsSource="Text goes here, or you could use a binding to a string" />
This simply lays out the text vertically by recognizing the fact that the string is an IEnumerable and so ItemsControl can treat each character in the string as a separate item. The default panel for ItemsControl is a StackPanel, so the characters are laid out vertically.
Note: For precise control over horizontal positioning, vertical spacing, etc, the ItemContainerStyle and ItemTemplate properties can be set on the ItemsControl.
Just in case anybody still comes across this post... here is a simple 100% xaml solution.
<TabControl TabStripPlacement="Left">
<TabItem Header="Tab 1">
<TabItem.LayoutTransform>
<RotateTransform Angle="-90"></RotateTransform>
</TabItem.LayoutTransform>
<TextBlock> Some Text for tab 1</TextBlock>
</TabItem>
<TabItem Header="Tab 2">
<TabItem.LayoutTransform>
<RotateTransform Angle="-90"></RotateTransform>
</TabItem.LayoutTransform>
<TextBlock> Some Text for tab 2</TextBlock>
</TabItem>
</TabControl>
I don't think there is a straighforward of doing this withought changing the way the system inherently laysout text. The easiest solution would be to change the width of the textblock and supply a few extra properties like this:
<TextBlock TextAlignment="Center" FontSize="14" FontWeight="Bold" Width="10" TextWrapping="Wrap">THIS IS A TEST</TextBlock>
This is hacky, but it does work.
Just use a simple LayoutTransform..
<Label Grid.Column="0" Content="Your Text Here" HorizontalContentAlignment="Center">
<Label.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="90" />
<ScaleTransform ScaleX="-1" ScaleY="-1"/>
</TransformGroup>
</Label.LayoutTransform>
</Label>
It's doable:
Your TextBlock's TextAlignment property should be set to Center:
<TextBlock Name="textBlock1" TextAlignment="Center" Text="Stacked!" />
Then add NewLines between every character:
textBlock1.Text =
String.Join(
Environment.NewLine,
textBlock1.Text.Select(c => new String(c, 1)).ToArray());
(Uses System.Linq to create an array of strings from the individual characters in the original string. I'm sure there are other ways of doing that...)
Below XAML code changes the angle of text displayed in a textblock.
<TextBlock Height="14"
x:Name="TextBlock1"
Text="Vertical Bottom to Up" Margin="73,0,115,0" RenderTransformOrigin="0.5,0.5" >
<TextBlock.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-90"/>
<TranslateTransform/>
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
the accepted answer suggested by Ray Burns does not work for me on .net 4.0. Here is how I did it:
pull in the mscorlib
xmlns:s="clr-namespace:System;assembly=mscorlib"
put in your usercontrol/window/page resources
<s:String x:Key="SortString">Sort</s:String>
and use it like this
<ItemsControl ItemsSource="{Binding Source={StaticResource SortString}}" Margin="5,-1,0,0" />
hope it helps!
create a stackpanel with a bunch ot textblocks that take one char
make the text container's max width to allow for one char only and wrap the text:
<TextBlock TextWrapping="Wrap" MaxWidth="8" TextAlignment="Center" Text="stack" />
Make an image and fill the block with the image, use photoshop or something designed to manipulate text instead of fiddling in code ?
This code allows to have vertical text stacking and horizontal centered letters.
<ItemsControl Grid.Row="1"
Grid.Column="0"
ItemsSource="YOUR TEXT HERE"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
HorizontalAlignment="Center"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here's a way to insert a '\n' after every character in the text of the TextBlock, that way making it display vertically:
<TextBlock x:Name="VertTextBlock" Text="Vertical Text" Loaded="VertTextBlock_Loaded"></TextBlock>
Then, in the Loaded event handler, you say:
TextBlock tb = sender as TextBlock;
StringBuilder sb = new StringBuilder(tb.Text);
int len = tb.Text.Length * 2;
for (int i = 1; i < len; i += 2)
{
sb.Insert(i, '\n');
}
tb.Text = sb.ToString();
That solution was proposed by Lette, but I believe my implementation incurs less overhead.
<linebreak/> can be used to show data in two lines
You could also use the "RUN" binding
In the App.xaml file use something like this:
<Application x:Class="Some.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:commands="clr-namespace:Deridiam.Helper.Commands"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
ShutdownMode="OnMainWindowClose"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<commands:HorizontalToVertical x:Key="HorizontalToVertical_Command"></commands:HorizontalToVertical>
<ControlTemplate x:Key="VerticalCell" TargetType="ContentControl">
<TextBlock Text="{TemplateBinding Content}" Foreground="Black"
TextAlignment="Center" FontWeight="Bold" VerticalAlignment="Center"
TextWrapping="Wrap" Margin="0" FontSize="10">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding ConvertToVerticalCmd, Source={StaticResource HorizontalToVertical_Command}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TextBlock}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</ControlTemplate>
</Application.Resources>
Create the command class binded to the textblock using i:Interaction.Triggers on the Loaded event in the app.xaml example
namespace Deridiam.Helper.Commands
{
public class HorizontalToVertical
{
private ICommand _convertToVerticalCommand;
public ICommand ConvertToVerticalCmd =>
_convertToVerticalCommand ?? (_convertToVerticalCommand = new RelayCommand(
x =>
{
var tBlock = x as TextBlock;
var horizontalText = tBlock.Text;
tBlock.Text = "";
horizontalText.Select(c => c).ToList().ForEach(c =>
{
if (c.ToString() == " ")
{
tBlock.Inlines.Add("\n");
//tBlock.Inlines.Add("\n");
}
else
{
tBlock.Inlines.Add((new Run(c.ToString())));
tBlock.Inlines.Add(new LineBreak());
}
});
}));
}
}
Finally in the .xaml file where you want the vertical text to be shown
<ContentControl Width="15" Content="Vertical Text" Template="{StaticResource VerticalCell}">
</ContentControl>
Will result in:
Vertical Text
none of the above solutions solved my problem (some come close), so I'm here to post my solution and maybe help someone.
The accepted solution helped me, but the text is not aligned to the center.
<ItemsControl ItemsSource="{Binding SomeStringProperty, FallbackValue=Group 1}" Margin="5"
TextElement.FontSize="16"
TextElement.FontWeight="Bold"
TextBlock.TextAlignment="Center"
HorizontalAlignment="Center"
VerticalAlignment="Center" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding }" HorizontalAlignment="Center" />
</DataTemplate>
</ItemsControl.ItemTemplate>
I will offer a solution based on the converter:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace Converters
{
[ValueConversion(typeof(object), typeof(string))]
public class InsertLineBreakConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter != null)
value = parameter;
if (value == null)
return null;
if (!(value is string str))
str = value.ToString();
return string.Join(Environment.NewLine, (IEnumerable<char>) str);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public static InsertLineBreakConverter Instance { get; } = new InsertLineBreakConverter();
}
public class InsertLineBreakConverterExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
=> InsertLineBreakConverter.Instance;
}
}
Usage examples:
<TextBlock Text="{Binding Property, Converter={cnvs:InsertLineBreakConverter}}"/>
<TextBlock Text="{Binding Converter={cnvs:InsertLineBreakConverter}, ConverterParameter='Some Text'}"/>