2012-03-19 12 views
15

पर किसी दिए गए बिंदु पर केंद्र टेक्स्ट पर मेरे पास Controls.Canvas है और इसमें कई आकार हैं और दिए गए बिंदुओं पर केंद्रित टेक्स्ट लेबल जोड़ना चाहते हैं (मैं लेबल वाले शिखर वाले पेड़ को चित्रित कर रहा हूं)। WPF में प्रोग्रामेटिक रूप से ऐसा करने का सबसे आसान तरीका क्या है?एक WPF कैनवास

मैंने RenderTransform सेटिंग करने और Controls.Canvas.SetLeft आदि पर कॉल करने का प्रयास किया है, लेकिन न ही उस लेबल को स्थिति दें जहां मैं इसे चाहता हूं। डब्ल्यूपीएफ केवल बाएं, दाएं, ऊपर और नीचे निर्देशांक पर पोजीशनिंग का समर्थन करता है और किसी दिए गए समन्वय पर केंद्रित नहीं है और Width संपत्तिहै और ActualWidth संपत्ति 0.0 है जब मैं Canvas का निर्माण करता हूं।

उत्तर

14

आप लेबल के मार्जिन को लेबल के ActualWidth और ActualHeight पर बाध्य करके प्राप्त कर सकते हैं, और इन मानों को -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, इस तरह परिभाषित

सेट करनासे 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); 
} 

यह वैकल्पिक लाभ यह है कि संलग्न संपत्ति के प्रभाव दृश्य में दिखाई देता है है स्टूडियो डिजाइनर (जो मार्जिन प्रॉपर्टी सेट करते समय मामला नहीं है)।

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

लोग इन उत्तरों को देख सकते हैं और सोच सकते हैं कि प्रश्न की सरल आवश्यकताओं के लिए यह बहुत जटिल है, लेकिन यह आपको केवल एक लेबल को केंद्र में दिखाने का तरीका नहीं दिखा रहा है। यह कोड किसी ऐसे व्यवहार को परिभाषित कर रहा है जो किसी भी बिंदु पर 'फ्रेमवर्क एलिमेंट' को केन्द्रित करेगा और इसे केंद्रित रखेगा, भले ही उस तत्व का आकार बदल रहा हो। –

+0

यह वास्तव में अच्छी तरह से काम करता है। एक्सएएमएल से उपयोग है: <टेक्स्टब्लॉक टेक्स्ट = "टेक्स्ट" सेंटरऑनपॉइंट.केंटरपॉइंट = "100,100" /> पॉइंट पर "100,100" के स्वचालित रूपांतरण पर ध्यान दें। –

+1

पूरी तरह से काम करता है, यहां तक ​​कि गैर WPF XAML उदा। विंडोज 10 सार्वभौमिक, धन्यवाद! –

1

क्षमा करें जॉन, मैं कल ट्विटर पर आपके प्रश्न को समझ नहीं पाया। यहां मैं एफ # में कोशिश कर सकता हूं! @cammcad

#R @ "C: \ प्रोग्राम फ़ाइलें (x86) \ संदर्भ विधानसभाओं \ Microsoft \ फ्रेमवर्क \ v3.0 \ PresentationFramework.dll" #R "@ C: \ Program Files (x86) \ संदर्भ सभाओं \ Microsoft \ फ्रेमवर्क \ v3.0 \ WindowsBase.dll " #R @" C: \ प्रोग्राम फ़ाइलें (x86) \ संदर्भ विधानसभाओं \ Microsoft \ फ्रेमवर्क \ v3.0 \ PresentationCore.dll "

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 
संबंधित मुद्दे