2012-03-19 4 views
15

나는 여러 가지 모양을 가진 Controls.Canvas을 가지고 있으며 지정된 점에 중심을 둔 텍스트 레이블을 추가하려고합니다 (레이블이 지정된 꼭지점이있는 트리를 그려야합니다). WPF에서 프로그래밍 방식으로이 작업을 수행하는 가장 간단한 방법은 무엇입니까?WPF 캔버스의 주어진 지점에 가운데 맞춤 텍스트

RenderTransform을 설정하고 Controls.Canvas.SetLeft 등으로 전화를 걸었지만 원하는 위치에 레이블을 두지 않았습니다. WPF는 지정된 왼쪽, 오른쪽, 위쪽 및 아래쪽 좌표에서만 위치 지정을 지원하고 지정된 좌표를 중심으로하지 않으며 속성은 NaN이고 속성은 Canvas을 구성 할 때 0.0 속성입니다.

답변

14

레이블의 여백을 레이블의 ActualWidthActualHeight에 바인딩하고이 값에 -0.5를 곱하면됩니다. 그러면 라벨이 너비의 절반만큼 왼쪽으로 이동합니다. 라벨을 반 높이만큼 위로 움직입니다. 여기

은 예이다 :

XAML :

<Window x:Class="CenteredLabelTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:CenteredLabelTest" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.Resources> 
     <local:CenterConverter x:Key="centerConverter"/> 
    </Window.Resources> 
    <Canvas> 
     <TextBlock x:Name="txt" Canvas.Left="40" Canvas.Top="40" TextAlignment="Center" Text="MMMMMM"> 
      <TextBlock.Margin> 
       <MultiBinding Converter="{StaticResource centerConverter}"> 
         <Binding ElementName="txt" Path="ActualWidth"/> 
         <Binding ElementName="txt" Path="ActualHeight"/> 
       </MultiBinding> 
      </TextBlock.Margin> 
     </TextBlock> 
     <Rectangle Canvas.Left="39" Canvas.Top="39" Width="2" Height="2" Fill="Red"/> 
    </Canvas> 
</Window> 

붉은 사각형 하이라이트 좌표 (40, 40)의 라벨 "MMMMMM"가 중심이되는.

변환기 : 결과는 다음과 같습니다

public class CenterConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (values[0] == DependencyProperty.UnsetValue || values[1] == DependencyProperty.UnsetValue) 
     { 
      return DependencyProperty.UnsetValue; 
     } 

     double width = (double) values[0]; 
     double height = (double)values[1]; 

     return new Thickness(-width/2, -height/2, 0, 0); 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

는 :

public class Mover : DependencyObject 
{ 
    public static readonly DependencyProperty MoveToMiddleProperty = 
     DependencyProperty.RegisterAttached("MoveToMiddle", typeof (bool), typeof (Mover), 
     new PropertyMetadata(false, PropertyChangedCallback)); 

    public static void SetMoveToMiddle(UIElement element, bool value) 
    { 
     element.SetValue(MoveToMiddleProperty, value); 
    } 

    public static bool GetMoveToMiddle(UIElement element) 
    { 
     return (bool) element.GetValue(MoveToMiddleProperty); 
    } 

    private static void PropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
     FrameworkElement element = sender as FrameworkElement; 
     if (element == null) 
     { 
      return; 
     } 

     if ((bool)e.NewValue) 
     { 
      MultiBinding multiBinding = new MultiBinding(); 
      multiBinding.Converter = new CenterConverter(); 
      multiBinding.Bindings.Add(new Binding("ActualWidth") {Source = element}); 
      multiBinding.Bindings.Add(new Binding("ActualHeight") {Source = element}); 
      element.SetBinding(FrameworkElement.MarginProperty, multiBinding); 
     } 
     else 
     { 
      element.ClearValue(FrameworkElement.MarginProperty); 
     } 
    } 

} 
:

centered label

프로그래밍 그렇게하기 위해이 같은 연결된 속성 Mover.MoveToMiddle을 정의

설정 Mover.MoveToMiddle에서 true은 해당 프레임 워크 요소의 여백이 실제 너비와 높이에 자동으로 바인딩되어 프레임 워크 요소가 해당 중심점으로 이동한다는 것을 의미합니다.

이 같이 당신의 XAML 코드를 사용합니다 :

<Window x:Class="CenteredLabelTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:CenteredLabelTest" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.Resources> 
     <local:CenterConverter x:Key="centerConverter"/> 
    </Window.Resources> 
    <Canvas> 
     <TextBlock Canvas.Left="40" Canvas.Top="40" TextAlignment="Center" Text="MMMMMM" 
       local:Mover.MoveToMiddle="True"/> 
     <Rectangle Canvas.Left="39" Canvas.Top="39" Width="2" Height="2" Fill="Red"/> 
    </Canvas> 
</Window> 

대안은 RenderTransform 대신 Margin에 결합하는 것입니다. 이 경우, 컨버터는

return new TranslateTransform(-width/2, -height/2); 

을 반환과 연결된 속성의 콜백 메소드는이 라인을 포함됩니다 :

if ((bool)e.NewValue) 
{ 
    ... 
    element.SetBinding(UIElement.RenderTransformProperty, multiBinding); 
} 
else 
{ 
    element.ClearValue(UIElement.RenderTransformProperty); 
} 

이 대안이 연결된 속성의 효과는 시각에서 볼 수있는 장점이있다을 Studio 디자이너 (Margin 속성을 설정할 때 해당하지 않음)

7

바인딩이 적어지면서 작동합니다.

public class CenterOnPoint 
{ 
    public static readonly DependencyProperty CenterPointProperty = 
    DependencyProperty.RegisterAttached("CenterPoint", typeof (Point), typeof (CenterOnPoint), 
    new PropertyMetadata(default(Point), OnPointChanged)); 

    public static void SetCenterPoint(UIElement element, Point value) 
    { 
    element.SetValue(CenterPointProperty, value); 
    } 

    public static Point GetCenterPoint(UIElement element) 
    { 
    return (Point) element.GetValue(CenterPointProperty); 
    } 

    private static void OnPointChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
    var element = (FrameworkElement)d; 
    element.SizeChanged -= OnSizeChanged; 
    element.SizeChanged += OnSizeChanged; 
    var newPoint = (Point)e.NewValue; 
    element.SetValue(Canvas.LeftProperty, newPoint.X - (element.ActualWidth/2)); 
    element.SetValue(Canvas.TopProperty, newPoint.Y - (element.ActualHeight/2)); 
    } 

    private static void OnSizeChanged(object sender, SizeChangedEventArgs e) 
    { 
    var element = (FrameworkElement) sender; 
    var newPoint = GetCenterPoint(element); 
    element.SetValue(Canvas.LeftProperty, newPoint.X - (e.NewSize.Width/2)); 
    element.SetValue(Canvas.TopProperty, newPoint.Y - (e.NewSize.Height/2)); 
    } 
} 

그리고 이렇게 사용하면 ...

label.SetValue(CenterOnPoint.CenterPointProperty, new Point(100, 100)); 
+4

사람들 "PresentationCore.dll \ 참조 어셈블리 \ 마이크로 소프트 \ 프레임 워크 \ 3.0 이 답을 살펴보고 질문의 간단한 요구 사항에 대해 너무 복잡하다고 생각할 수도 있지만, 이는 레이블을 가운데에 배치하는 방법을 보여주는 것이 아닙니다. 이 코드는 포인트에 'FrameworkElement'를 중점에 배치하고 요소의 크기가 변경 될 때라도 중앙에 유지하는 동작을 정의합니다. –

+0

정말 잘 작동합니다. XAML에서 사용법은 다음과 같습니다. "100,100"을 Point로 자동 변환합니다. –

+1

비 WPF XAML에서도 완벽하게 작동합니다. Windows 10 universal, 감사합니다! –

1

죄송합니다. Jon, 전 어제 트위터에서 질문을 이해하지 못했습니다. 다음은 F #에서 사용하는 방법입니다. C "@ #R : \의 Program Files (x86) \ 참조 : @"\ PresentationFramework.dll \ 마이크로 소프트 \ 프레임 워크 \ 3.0 \의 Program Files (x86)를 \ 참조 어셈블리 C "@cammcad

#R 마이크로 소프트 \ 프레임 워크 \ 3.0 \ 수 WindowsBase.dll " #R @"\ 어셈블리 C : \의 Program Files (x86) \

open System 
open System.IO 
open System.Windows 
open System.Windows.Shapes 
open System.Windows.Media 
open System.Windows.Controls 
open System.Windows.Markup 
open System.Xml 

(* Add shape and label to canvas at specific location *) 
let addShapeAndLabel_at_coordinate (label: string) (coordinate: float * float) (c: Canvas) = 
    let btn = Button(Content=label,Foreground=SolidColorBrush(Colors.White)) 
    let template = 
    "<ControlTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' 
     TargetType=\"Button\">" + 
     "<Grid>" + 
     " <Ellipse Width=\"15\" Height=\"15\" Fill=\"Orange\" HorizontalAlignment=\"Center\"/>" + 
     " <ContentPresenter HorizontalAlignment=\"Center\" " + "VerticalAlignment=\"Center\"/> " + 
     "</Grid>" + 
     "</ControlTemplate>" 

    btn.Template <- XamlReader.Parse(template) :?> ControlTemplate 
    c.Children.Add(btn) |> ignore 
    let textsize = 
     FormattedText(label,CultureInfo.GetCultureInfo("enus"), 
     FlowDirection.LeftToRight,Typeface("Verdana"),32.0,Brushes.White) 
     |> fun x -> x.MinWidth, x.LineHeight 
    let left,top = coordinate 
    let middle_point_width = fst(textsize)/2.0 
    let middle_point_height = snd(textsize)/2.0 
    Canvas.SetLeft(btn,left - middle_point_width) 
    Canvas.SetTop(btn,top - middle_point_height) 

let shell = new Window(Width=300.0,Height=300.0) 
let canvas = new Canvas(Width=300.0,Height=300.0,Background=SolidColorBrush(Colors.Green)) 

addShapeAndLabel_at_coordinate "Tree Node 1" (100.0,50.0) canvas 
addShapeAndLabel_at_coordinate "TreeNode 2" (150.0, 75.) canvas 
shell.Content <- canvas 

[<STAThread>] ignore <| (new Application()).Run shell