JAAAAY
JAAAAY

Reputation: 11

Add Text to a System.Windows.Shapes.Shape Code Behind,

I need to return a Text as a System.Window.Shapes Shape (dont ask why, its the task). Since there is no Text-Shape, I thought I could add a Text to an transparent Rectangle, but turns out, with all what I found on the net, the produced text just does not look nice but what is even worse: its drawn with some nasty artefacts:

It kind of looks like the upper top of the Text is cut and appended at the bottom (like some overflow of text)

this is the way I've done it (Need to add text to rectangle):

  Rectangle r = new Rectangle();
  r.Stroke = Brushes.Transparent;
  r.StrokeThickness = 1;
  r.VerticalAlignment = VerticalAlignment.Top;
  r.HorizontalAlignment = HorizontalAlignment.Left;
  r.Margin = new Thickness(0);
  r.Width = 200;
  r.Height = 200;
  r.RenderTransform = new TranslateTransform(100, 100);
  TextBlock TB = new TextBlock();
  TB.Text = "TEST";
  var ft = new FormattedText(
               TB.Text,
               CultureInfo.CurrentCulture,
               FlowDirection.LeftToRight,
               new Typeface("Verdana"),
               16,
               Brushes.Black);
  r.Width = ft.Width;
  r.Height = ft.Height;
  //The next two magical lines create a special brush that contains a bitmap rendering of the UI element that can then be used like any other brush and its in hardware and is almost the text book example for utilizing all hardware rending performances in WPF unleashed 4.5
  BitmapCacheBrush bcb = new BitmapCacheBrush(TB);
  r.Fill = bcb;

Can somebody explain why this is happening and maybe give a workaround? Tried to play with Thickness, Padding, etc. but it didnt give the desired result. I would appreciate any help! Thanks already

Upvotes: 0

Views: 889

Answers (2)

JAAAAY
JAAAAY

Reputation: 11

As much as I appreciate all the answers given, I've found the solution:

simply set the fontsize of the Textblock to the same size that you use in formattedText to calculate the Text width and Height:

tb.Fontsize = 16; ...

this way not only does the text "overflow" disappear, but the text looks nice and sharp

Upvotes: 0

Sinatr
Sinatr

Reputation: 22008

It's very easy to achieve what you want using MVVM and datatemplates. Below is a simplified demo to give you idea.

xaml:

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type local:TextItem}">
            <TextBlock Text="{Binding Text}" />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:ShapeItem}">
            <ContentControl Content="{Binding Shape}" />
        </DataTemplate>
    </ItemsControl.Resources>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding Left}" />
            <Setter Property="Canvas.Top" Value="{Binding Top}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

cs:

public class Item
{
    public double Left { get; set; }
    public double Top { get; set; }
}

public class ShapeItem : Item
{
    public Shape Shape { get; set; }
}

public class TextItem : Item
{
    public string Text { get; set; }
}

public partial class MainWindow : Window
{
    public List<object> Items { get; } = new List<object>
    {
        new ShapeItem { Shape = new Rectangle { Width = 100, Height = 100, Fill= Brushes.Yellow, Stroke = Brushes.Red }, Left = 10, Top = 10 },
        new TextItem { Text= "Lalala", Left = 50, Top = 50 },
    };

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }
}

Here is the output:

It's not 100% MVVM (for the sake of simplicity), but idea is to have different data template for text and for shapes. They have different viewmodels: TextItem and ShapeItem and are visualized as TextBlock or ContentControl correspondingly.

Another thing is to use ItemsControl to be able to bind to collection of Items and set Canvas.Left/Canvas.Top properties.

ToDo: use ObservableCollection, implement INotifyPropertyChanged for viewmodels, do not use view elements in the viewmodel (brushes, shapes, etc.).

Upvotes: 2

Related Questions