Entwickler-Ecke

WPF / Silverlight - Path relativ zu Parent zeichnen


FrEEzE2046 - So 06.06.10 00:58
Titel: Path relativ zu Parent zeichnen
Hallo,

ich habe folgenden XML Code:

XML-Daten
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
  x:Class="MainPage"
  Width="640" Height="480">

  <Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
      <RowDefinition Height="0.1*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.1*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="0.1*"/>
      <ColumnDefinition Width="0.1*"/>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="0.1*"/>
      <ColumnDefinition Width="0.1*"/>
    </Grid.ColumnDefinitions>

    <Path Grid.Row="2" Grid.Column="2" Stroke="Red" StrokeThickness="1">
      <Path.Data>
        <PathGeometry>
          <PathFigure StartPoint="0, 0" IsClosed="False">
            <LineSegment Point="0, 100"/>
          </PathFigure>
        </PathGeometry>
      </Path.Data>
    </Path>    
  </Grid>
</UserControl>


Es soll im entsprechenden Grid-Feld eine Linie von oben bis unten gezeichnet werden. Leider sind die Point-Angaben von PathGeometry jedoch in Pixeln. Egal wie groß das Grid-Feld jedoch ist, soll der Path natürlich immer gleich aussehen. Wie kann man sowas machen?


Kha - So 06.06.10 10:42

Nimm ein Line-Objekt, bei dem solltest du Y2 an seine eigene ActualHeight binden können. Komplexere Berechnungen müsste man wahrscheinlich in Code ausführen.


FrEEzE2046 - So 06.06.10 21:39

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Nimm ein Line-Objekt, bei dem solltest du Y2 an seine eigene ActualHeight binden können. Komplexere Berechnungen müsste man wahrscheinlich in Code ausführen.


Also die Figur ist schon etwas komplizierter und ich brauche auch BezierSegment usw. Daher kann ich Line nicht einfach verwenden.

Ich habe in der gleichen Grid-Zelle auch ein anderes Control liegen, das den ganzen Platz der Zelle nutzt. Ich kann daher als repräsentativen Wert dessen ActualWidth und ActualHeight.

Ich hab mir eine IValueConverter Klasse geschrieben, die mir einen double Wert mit einem Faktor multipliziert.

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
public class MultiplicationHelper : IValueConverter 
{
    private double _factor = 1.0;

    public double Factor
    {
        get { return this._factor; }
        set { this._factor = value; }
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value.GetType() == typeof(double) && targetType == typeof(double))
            return ((double)value) * this._factor;
        else
            throw new NotSupportedException();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value.GetType() == typeof(double) && targetType == typeof(double))
            return ((double)value) / this._factor;
        else
            throw new NotSupportedException();
    }
}


Ich brauche jedoch verschiedene Faktoren von oben genannten Werten. Um das jetzt - mit meinem Kentnissstand - umzusetzen, bräuchte ich also mehrfache Instanzen von dieser Klasse:


XML-Daten
1:
2:
3:
4:
5:
<Path.Resources>
    <helpers:MultiplicationHelper x:Key="multiHelper1" Factor="0.25"/>
  <helpers:MultiplicationHelper x:Key="multiHelper2" Factor="0.5"/>
  <helpers:MultiplicationHelper x:Key="multiHelper3" Factor="0.75"/>
</Path.Resources>


Ist es nicht einfach möglich eine Instanz des Converters zu nehmen und dem Converter-Attribute von Binding diesen als Parameter zu übergeben? Oder muss ich wirklich ganz viele Objekte erstellen um meine kleine Multiplikation im XML-Code zu definieren?


Kha - So 06.06.10 22:45

Soll nun eine Zeichung gleichmäßig skaliert werden oder was? Dann hätte ich noch eine andere Idee: Das Path-Objekt mit RenderTransform auf das x-fache und die Stiftgröße auf das 1/x-fache skalieren :) .

user profile iconFrEEzE2046 hat folgendes geschrieben Zum zitierten Posting springen:
Ist es nicht einfach möglich eine Instanz des Converters zu nehmen und dem Converter-Attribute von Binding diesen als Parameter zu übergeben?
Doch doch, dazu ist Binding.ConverterParameter da ;) .


FrEEzE2046 - So 06.06.10 22:48

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Das Path-Objekt mit RenderTransform auf das x-fache und die Stiftgröße auf das 1/x-fache skalieren :)


Kannst du das etwas genauer erläutern? Das x-fache von was?


Kha - So 06.06.10 23:00

Das war ja meine Frage. Wenn es einen gleichmäßigen Faktor gibt, der irgendwie von ActualWidth/Height abhängt, dann ist der x :) .


FrEEzE2046 - Mo 07.06.10 09:50

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Das war ja meine Frage. Wenn es einen gleichmäßigen Faktor gibt, der irgendwie von ActualWidth/Height abhängt, dann ist der x :) .


Okay,

also die Path-Größe steht in Relation zum Image. D.h. auf ActualWidth, ActualHeightdes Bildes kann ich zugreifen.


FrEEzE2046 - Mo 07.06.10 11:08

Also,

auf deinen Hinweis hin, habe ich es jetzt über RenderTransform "gelöst". Das funktioniert für die Anpassung der Höhe wunderbar. Die Breite wird jedoch - merkwürdigerweiße - nicht korrekt gesetzt. Das Path Objekt ist reicht nur bis ungefähr in die Mitte des Bildes. Warum?



C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
public class DivisionHelper : IValueConverter
{
    public double Divider { get; set; }
            
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
        if (parameter != null && parameter.GetType() == typeof(double))
            this.Divider = (double)parameter;

        if (value.GetType() == typeof(double) && targetType == typeof(double))
            return ((double)value) / this.Divider;
        else
            throw new NotSupportedException();
  }
    
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:helpers="clr-namespace:Helpers"
  x:Class="MainPage"
  Width="640" Height="480" Loaded="UserControl_Loaded">
    

  <Grid x:Name="LayoutRoot" Background="White">
        <Grid.Resources>
            <helpers:DivisionHelper x:Key="divHelper" Divider="100.0"/>
        </Grid.Resources>
        
    <Grid.RowDefinitions>
      <RowDefinition Height="0.1*"/>
      <RowDefinition Height="0.08*"/>
      <RowDefinition Height="*"/>
      <RowDefinition Height="0.08*"/>
      <RowDefinition Height="0.1*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="0.1*"/>
      <ColumnDefinition Width="0.05*"/>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="0.05*"/>
      <ColumnDefinition Width="0.1*"/>      
    </Grid.ColumnDefinitions>
    
    <Rectangle Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Grid.RowSpan="3" 
           Stroke="Black" RadiusY="45" RadiusX="135">
      
      <Rectangle.Fill>
        <LinearGradientBrush>
          <GradientStop Offset="0" Color="Azure"/>
          <GradientStop Offset="0.5" Color="White"/>
          <GradientStop Offset="1" Color="Azure"/>
        </LinearGradientBrush>
      </Rectangle.Fill>    
    </Rectangle>
    
    <Image x:Name="img" Grid.Row="2" Grid.Column="2" Source="Images/beispielbild_320x480.jpg"/>
           
    <Path Grid.Row="2" Grid.Column="2">
            <Path.RenderTransform>
                <ScaleTransform CenterX="0.5" CenterY="0.5"
                                ScaleX="{Binding ElementName=img, Path=ActualWidth, Converter={StaticResource divHelper}}"
                                ScaleY="{Binding ElementName=img, Path=ActualHeight, Converter={StaticResource divHelper}}"/>
            </Path.RenderTransform>
      <Path.Data>
        <PathGeometry>
          <PathFigure StartPoint="0, 0" IsClosed="True">
            <LineSegment Point="0, 50"/>
            <BezierSegment Point1="25, 75" Point2="75, 75" Point3="100, 50"/>
            <LineSegment Point="100, 0"/>
          </PathFigure>
        </PathGeometry>
      </Path.Data>
      <Path.Fill>
        <SolidColorBrush Color="Red" Opacity="0.2"/>
      </Path.Fill>
    </Path>
    
    <Path Grid.Row="2" Grid.Column="2">
            <Path.RenderTransform>
                <ScaleTransform CenterX="0.5" CenterY="0.5"
                                ScaleX="{Binding ElementName=img, Path=ActualWidth, Converter={StaticResource divHelper}}"
                                ScaleY="{Binding ElementName=img, Path=ActualHeight, Converter={StaticResource divHelper}}"/>
            </Path.RenderTransform>
      <Path.Data>
        <PathGeometry>
          <PathFigure StartPoint="0, 50" IsClosed="True">
                        <BezierSegment Point1="25, 75" Point2="75, 75" Point3="100, 50"/>
                        <LineSegment Point="100, 100"/>
            <LineSegment Point="0, 100"/>
          </PathFigure>
        </PathGeometry>
      </Path.Data>
      <Path.Fill>
        <SolidColorBrush Color="Green" Opacity="0.2"/>
      </Path.Fill>
    </Path>
  </Grid>
</UserControl>