F
You don't need a caste form control. You just need to paint a cloud with a tail of the right size and put the text inside.Let's take the original picture and get the text out of it: It'll be our back. Just need him to change the sizes differently. The standard idea is to focus on several areas: The angles shall not change at all, the upper and lower side shall be horizontally stretched, left and right side by vertical, and the central shall be in both directions. When the top and bottom side pictures are still stretched in the width, they can be extended in a single picsel, and the same left and right side, one picsel height. The central area can only be 1x1.To implement this, the WPF will be UserControl:<UserControl x:Class="ChatInterface.Bubble"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<!-- сетка 3х3 -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="11"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="17"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="11"/>
<RowDefinition Height="*"/>
<RowDefinition Height="11"/>
</Grid.RowDefinitions>
<!-- левый верхний уголок -->
<Image Source="bubbleNW.png" Grid.Row="0" Grid.Column="0"
RenderOptions.EdgeMode="Aliased" Stretch="None"/>
<!-- верхняя полоса -->
<Image Source="bubbleN.png" Grid.Row="0" Grid.Column="1"
RenderOptions.EdgeMode="Aliased" Stretch="Fill"/>
<!-- правый верхний уголок -->
<Image Source="bubbleNE.png" Grid.Row="0" Grid.Column="2"
RenderOptions.EdgeMode="Aliased" Stretch="None"/>
<!-- левая полоса -->
<Image Source="bubbleW.png" Grid.Row="1" Grid.Column="0"
RenderOptions.EdgeMode="Aliased" Stretch="Fill"/>
<!-- центр -->
<Image Source="bubbleC.png" Grid.Row="1" Grid.Column="1"
RenderOptions.EdgeMode="Aliased" Stretch="Fill"/>
<!-- и т. д. -->
<Image Source="bubbleE.png" Grid.Row="1" Grid.Column="2"
RenderOptions.EdgeMode="Aliased" Stretch="Fill"/>
<Image Source="bubbleSW.png" Grid.Row="2" Grid.Column="0"
RenderOptions.EdgeMode="Aliased" Stretch="None"/>
<Image Source="bubbleS.png" Grid.Row="2" Grid.Column="1"
RenderOptions.EdgeMode="Aliased" Stretch="Fill"/>
<Image Source="bubbleSE.png" Grid.Row="2" Grid.Column="2"
RenderOptions.EdgeMode="Aliased" Stretch="None"/>
</Grid>
</UserControl>
Now, we need to put it on. A perfectly correct path would be to make a decorator with a newly created counterfoil in the background, but that would require advanced WPF knowledge, so we'll go a simpler way: one more. UserControl♪ A little trick is that clouds should be as large as the text, so you're gonna have to use Binding, and that Bubble doesn't have to influence the layout itself, put it in, for example, Canvas:<UserControl x:Class="ChatInterface.BubbleWithText"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ChatInterface"
Name="root">
<Grid>
<Canvas>
<local:Bubble
Height="{Binding ActualHeight, ElementName=tb}"
Width="{Binding ActualWidth, ElementName=tb}"/>
</Canvas>
<TextBlock Name="tb" Padding="11" TextWrapping="Wrap"
Text="{Binding Text, ElementName=root}"/>
</Grid>
</UserControl>
In code-behind, you have to put your properties on. Text:public partial class BubbleWithText : UserControl
{
public BubbleWithText()
{
InitializeComponent();
}
#region dp string Text
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(BubbleWithText));
#endregion
}
Now we can put our elements in. StackPanel: <StackPanel Width="150">
<local:BubbleWithText Text="Я думаю, она дала тебе неправильный номер"/>
<local:BubbleWithText Text="А может быть и нет, кто их разберёт?"/>
</StackPanel>
and obtain the following: Of course, the original pictures are best painted on their own (or demanded by the designer) and in no case retained in JPEG.If you want to do the right thing, use the decorator:public class BubbleDecorator : Decorator
{
Bubble bubble = new Bubble();
public BubbleDecorator()
{
bubble.SetBinding(Bubble.WidthProperty, new Binding("ActualWidth")
{ Source = this });
bubble.SetBinding(Bubble.HeightProperty, new Binding("ActualHeight")
{ Source = this });
}
protected override Visual GetVisualChild(int index)
{
if (Child != null)
{
if (index == 0)
return bubble;
if (index == 1)
return Child;
}
throw new IndexOutOfRangeException("Wrong child index");
}
protected override Size ArrangeOverride(Size arrangeSize)
{
bubble.Arrange(new Rect(arrangeSize));
return base.ArrangeOverride(arrangeSize);
}
protected override int VisualChildrenCount { get { return Child == null ? 0 : 2; } }
}
We get this XAML:<StackPanel Width="150">
<local:BubbleDecorator HorizontalAlignment="Right" Margin="2">
<TextBlock Padding="13" TextWrapping="Wrap"
Text="Я думаю, она дала тебе неправильный номер"/>
</local:BubbleDecorator>
<local:BubbleDecorator HorizontalAlignment="Right" Margin="2">
<TextBlock Padding="13" TextWrapping="Wrap"
Text="А может быть и нет, кто их разберёт?"/>
</local:BubbleDecorator>
<local:BubbleDecorator HorizontalAlignment="Right" Margin="2">
<TextBlock Padding="13" TextWrapping="Wrap"
Text="Приффки!!1"/>
</local:BubbleDecorator>
</StackPanel>