画布子项不移动位置

发布于 2024-12-08 11:22:47 字数 5251 浏览 0 评论 0原文

Tribe,

我构建了一个 Silverlight 自定义控件 TextBubble,它是网格内的椭圆和文本块。我根据需要通过代码将这些文本气泡添加到画布对象中。当我这样做时,TextBubble 的属性已设置,但当对象显示在画布上时不会反映出来。

如果我手动构建相同的结构并将其添加到画布,canvas.Children.Add(grid),则该子项会正确显示。

使用 TextBubble,文本和椭圆显示,但直径和 X、Y 尚未应用,气泡显示在画布的左上角。

感谢您的帮助, -AM

有效的代码:

Grid g = new Grid();
g.SetValue(Canvas.TopProperty, 100.0);
g.SetValue(Canvas.LeftProperty, 100.0);
Ellipse e = new Ellipse();
e.Width = 50;
e.Height = 50;
e.Fill = new SolidColorBrush(Colors.Green);
g.Children.Add(e);
TextBlock t = new TextBlock();
t.Text = "TEST";
t.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
t.VerticalAlignment = System.Windows.VerticalAlignment.Center;
g.Children.Add(t);
this.canvas.Children.Add(g);

无效的代码:

TextBubble bubble = new TextBubble();
bubble.Text = "TEST";
bubble.Diameter = 50;
bubble.Fill = new SolidColorBrush(Colors.Green);
bubble.X = 100;
bubble.Y = 100;
canvas.Children.Add(bubble);

TextBubble 对象:

public class TextBubble : Control {

public TextBubble() {
    this.DefaultStyleKey = typeof(TextBubble);
}

public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        "Text",
        typeof(string),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnTextChanged)));

public static readonly DependencyProperty DiameterProperty = DependencyProperty.Register(
        "Diameter",
        typeof(int),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnDiameterChanged)));

public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
        "Fill",
        typeof(Brush),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnFillChanged)));

public static readonly DependencyProperty XCoordProperty = DependencyProperty.Register(
        "X",
        typeof(double),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnXCoordChanged)));

public static readonly DependencyProperty YCoordProperty = DependencyProperty.Register(
        "Y",
        typeof(double),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnYCoordChanged)));


public string Text {
    get { return (string)this.GetValue(TextProperty); }
    set { base.SetValue(TextProperty, value); }
}

public int Diameter {
    get { return (int)this.GetValue(DiameterProperty); }
    set { base.SetValue(DiameterProperty, value); }
}

public Brush Fill {
    get { return (Brush)this.GetValue(FillProperty); }
    set { base.SetValue(FillProperty, value); }
}

public double X {
    get { return (double)this.GetValue(XCoordProperty); }
    set { base.SetValue(XCoordProperty, value); }
}

public double Y {
    get { return (double)this.GetValue(YCoordProperty); }
    set { base.SetValue(YCoordProperty, value); }
}
}

Generic.xml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:X4S.Controls" >
<Style TargetType="local:TextBubble">
    <Setter Property="Text" Value="ABC" />
    <Setter Property="Diameter" Value="50" />
    <Setter Property="X" Value="100.0" />
    <Setter Property="Y" Value="100.0" />
    <Setter Property="Fill" Value="GhostWhite" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:TextBubble">
                <Grid x:Name="LayoutRoot" Canvas.Top="{TemplateBinding Y}" Canvas.Left="{TemplateBinding X}" >
                    <Ellipse Fill="{TemplateBinding Fill}" Width="{TemplateBinding Diameter}" Height="{TemplateBinding Diameter}"/>
                    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Text}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
</ResourceDictionary>

明白了!

当向画布子树添加新的自定义控件时,我们将这个额外的东西(TextBubble)包裹在 grid-textblock-ellipse 包中。好吧,TextBubble 对象从未设置过其位置或大小。因此,即使 Text 和 Fill 应用于内部控件,这些属性也不会受到 TextBubble 的位置或大小的影响。

一旦我们将相同的位置和大小属性应用到 TextBubble 本身 - 瞧,它就起作用了。

关键是: ((Control)canvas.GetDescendant()).GetDescendant().GetValue(Canvas.LeftProperty) 200.0

这表明网格确实设置了位置 - 但其父级 TextBubble 没有设置。

解决办法:

private static void OnDiameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    TextBubble textBubble = d as TextBubble;
    textBubble.Width = (int)e.NewValue;
    textBubble.Height = (int)e.NewValue;
}

private static void OnLeftChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    TextBubble textBubble = d as TextBubble;
    textBubble.SetValue(Canvas.LeftProperty, e.NewValue);
}

private static void OnTopChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    TextBubble textBubble = d as TextBubble;
    textBubble.SetValue(Canvas.TopProperty, e.NewValue);
}

可能有更好的方法(我会研究它),但这有效。

Tribe,

I have built a Silverlight custom control, TextBubble, which is an ellipse and textblock inside a grid. I'm adding these text bubbles to a canvas object through code on demand. When I do so, the properties of the TextBubble are set but are not reflected when the objects show up on the canvas.

If I build the same structure manually and add the to the canvas, canvas.Children.Add(grid), this child shows up correctly.

Using the TextBubble, the text and ellipse show up but the diameter and the X,Y have not bee applied, the bubble shows up in the top left of the canvas.

Thanks for any help,
-AM

Code that works:

Grid g = new Grid();
g.SetValue(Canvas.TopProperty, 100.0);
g.SetValue(Canvas.LeftProperty, 100.0);
Ellipse e = new Ellipse();
e.Width = 50;
e.Height = 50;
e.Fill = new SolidColorBrush(Colors.Green);
g.Children.Add(e);
TextBlock t = new TextBlock();
t.Text = "TEST";
t.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
t.VerticalAlignment = System.Windows.VerticalAlignment.Center;
g.Children.Add(t);
this.canvas.Children.Add(g);

Code that does not:

TextBubble bubble = new TextBubble();
bubble.Text = "TEST";
bubble.Diameter = 50;
bubble.Fill = new SolidColorBrush(Colors.Green);
bubble.X = 100;
bubble.Y = 100;
canvas.Children.Add(bubble);

TextBubble object:

public class TextBubble : Control {

public TextBubble() {
    this.DefaultStyleKey = typeof(TextBubble);
}

public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        "Text",
        typeof(string),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnTextChanged)));

public static readonly DependencyProperty DiameterProperty = DependencyProperty.Register(
        "Diameter",
        typeof(int),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnDiameterChanged)));

public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
        "Fill",
        typeof(Brush),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnFillChanged)));

public static readonly DependencyProperty XCoordProperty = DependencyProperty.Register(
        "X",
        typeof(double),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnXCoordChanged)));

public static readonly DependencyProperty YCoordProperty = DependencyProperty.Register(
        "Y",
        typeof(double),
        typeof(TextBubble),
        new PropertyMetadata(new PropertyChangedCallback(OnYCoordChanged)));


public string Text {
    get { return (string)this.GetValue(TextProperty); }
    set { base.SetValue(TextProperty, value); }
}

public int Diameter {
    get { return (int)this.GetValue(DiameterProperty); }
    set { base.SetValue(DiameterProperty, value); }
}

public Brush Fill {
    get { return (Brush)this.GetValue(FillProperty); }
    set { base.SetValue(FillProperty, value); }
}

public double X {
    get { return (double)this.GetValue(XCoordProperty); }
    set { base.SetValue(XCoordProperty, value); }
}

public double Y {
    get { return (double)this.GetValue(YCoordProperty); }
    set { base.SetValue(YCoordProperty, value); }
}
}

Generic.xml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:X4S.Controls" >
<Style TargetType="local:TextBubble">
    <Setter Property="Text" Value="ABC" />
    <Setter Property="Diameter" Value="50" />
    <Setter Property="X" Value="100.0" />
    <Setter Property="Y" Value="100.0" />
    <Setter Property="Fill" Value="GhostWhite" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:TextBubble">
                <Grid x:Name="LayoutRoot" Canvas.Top="{TemplateBinding Y}" Canvas.Left="{TemplateBinding X}" >
                    <Ellipse Fill="{TemplateBinding Fill}" Width="{TemplateBinding Diameter}" Height="{TemplateBinding Diameter}"/>
                    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Text}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
</ResourceDictionary>

Got it!

When adding a new custom control to the canvas child tree we got this extra thing (the TextBubble) wrapped around the grid-textblock-ellipse bundle. Well, the TextBubble object never had its location or size set. So even though the Text and Fill were being applied to the internal controls, those properties were not impacted by the TextBubble's location or size.

Once we applied the same location and size attributes to the TextBubble itself - voila it worked.

The key was this:
((Control)canvas.GetDescendant()).GetDescendant().GetValue(Canvas.LeftProperty)
200.0

Which showed that the grid really DID have its location set - but its parent, the TextBubble , did not.

The fix:

private static void OnDiameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    TextBubble textBubble = d as TextBubble;
    textBubble.Width = (int)e.NewValue;
    textBubble.Height = (int)e.NewValue;
}

private static void OnLeftChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    TextBubble textBubble = d as TextBubble;
    textBubble.SetValue(Canvas.LeftProperty, e.NewValue);
}

private static void OnTopChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    TextBubble textBubble = d as TextBubble;
    textBubble.SetValue(Canvas.TopProperty, e.NewValue);
}

There might be a better way (I'll research it) but this worked.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

紫﹏色ふ单纯 2024-12-15 11:22:47

这是工作解决方案,我刚刚检查过。但你仍然有两个选择:
1. 在构造函数中设置 DataContext 并使用常规 Bindings
2. 不要设置DataContext并使用RelativeSource作为源(请参阅下面的XAML)

Canvas.Left & 并不完全明显Canvas.Top 应设置为 canvas 的直接子级,即 local:TextBubble。

<Style TargetType="local:TextBubble">
        <Setter Property="Text" Value="ABC" />
        <Setter Property="Diameter" Value="50" />


        <Setter Property="Canvas.Top" 
                Value="{Binding Path=Y, RelativeSource={RelativeSource Self}}" />
        <Setter Property="Canvas.Left" 
                Value="{Binding Path=X, RelativeSource={RelativeSource Self}}" />

        <Setter Property="Fill" Value="GhostWhite" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:TextBubble">
                    <Grid x:Name="LayoutRoot" >
                        <Ellipse Fill="{TemplateBinding Fill}" Width="{TemplateBinding Diameter}" Height="{TemplateBinding Diameter}"/>
                        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Text}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Here's the working solution, I've just checked it. Still you've got two choices:
1. Set DataContext in constructor and use regular Bindings
2. Don't set DataContext and use RelativeSource as your source (see the XAML below)

It's not exactly obvious that Canvas.Left & Canvas.Top should be set for the immediate child of canvas, which is local:TextBubble.

<Style TargetType="local:TextBubble">
        <Setter Property="Text" Value="ABC" />
        <Setter Property="Diameter" Value="50" />


        <Setter Property="Canvas.Top" 
                Value="{Binding Path=Y, RelativeSource={RelativeSource Self}}" />
        <Setter Property="Canvas.Left" 
                Value="{Binding Path=X, RelativeSource={RelativeSource Self}}" />

        <Setter Property="Fill" Value="GhostWhite" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:TextBubble">
                    <Grid x:Name="LayoutRoot" >
                        <Ellipse Fill="{TemplateBinding Fill}" Width="{TemplateBinding Diameter}" Height="{TemplateBinding Diameter}"/>
                        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Text}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文