2010-02-16 29 views
6

Tôi đang tìm hiểu về mẫu điều khiển trong WPF và kiểm tra cách thay thế giao diện nút bằng kiểu mẫu tùy chỉnh. Tôi thấy rằng để tạo ra một nút tròn, một Ellipse phải được xác định với cùng chiều cao và chiều rộng.Mẫu điều khiển nút có vòng tròn có thể thay đổi kích thước

<Style x:Key="Button2" TargetType="Button"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="Button"> 
       <Grid> 
        <Ellipse Fill="LightGreen" Width="80" Height="80"/> 
        <ContentPresenter HorizontalAlignment="Center" 
VerticalAlignment="Center"/> 
       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
    <Setter Property="Control.Margin" Value="10"/> 
</Style> 

Tất nhiên, điều đó chỉ buộc tất cả các nút sử dụng kiểu đó để có đường tròn có đường kính 80 pixel, bất kể nút được đổi kích thước như thế nào. Tôi muốn cho vòng tròn tiếp tục với các giá trị chiều cao/chiều rộng ngắn hơn để nó có thể tự động mở rộng quy mô theo kích thước nút.

Tuy nhiên tôi chưa đọc bất kỳ tài liệu nào dạy cách thực hiện điều này trong mẫu XAML thuần túy? Có vẻ như một số code-behind là cần thiết để đạt được hiệu ứng này?

Trả lời

6

Đây là nơi TemplateBinding xuất hiện (TemplateBinding được sử dụng bên trong mẫu điều khiển và được sử dụng để truy xuất các giá trị từ điều khiển khuôn mẫu, trong trường hợp này là Nút).

<Ellipse Fill="LightGreen" 
    Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}"/> 

Lưu ý rằng đây là một hình thức ngắn hơn của việc sử dụng:

{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}} 

Phần mở rộng đánh dấu TemplateBinding chỉ được tối ưu hóa cho chỉ bindings TemplatedParent.

Điều đó nói rằng, nếu bạn muốn nó chỉ là một vòng tròn, nếu hình elip của bạn nhỏ hơn W/H, thì nội dung của bạn sẽ dễ dàng thoát ra khỏi nó, mà tôi nghi ngờ là những gì bạn thực sự muốn ..? Tôi đã nghĩ đến việc sử dụng một bộ chuyển đổi giá trị đa để làm điều đó, nhưng bạn không thể liên kết với thông số chuyển đổi, vì vậy đó là ra ngoài.

Trong trường hợp đó, hành vi được đính kèm sẽ hoạt động nhưng không đẹp.

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:c="clr-namespace:WpfApplication1" 
    xmlns:local="clr-namespace:WpfApplication1" 
    Title="Window1" Height="300" Width="300"> 

    <Grid> 
     <Button Content="Yo!" Width="50" Height="30"> 
      <Button.Template> 
       <ControlTemplate TargetType="Button"> 
        <Grid> 
         <Ellipse Fill="LightGreen" local:ConstrainWidthHeight.ConstrainedWidth="{TemplateBinding ActualWidth}" local:ConstrainWidthHeight.ConstrainedHeight="{TemplateBinding ActualHeight}"/> 
         <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> 
        </Grid> 
       </ControlTemplate> 
      </Button.Template> 
     </Button> 
    </Grid> 
</Window> 

... và hành vi kèm theo:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows; 

namespace WpfApplication1 { 
    public class ConstrainWidthHeight { 
     public static readonly DependencyProperty ConstrainedWidthProperty = 
      DependencyProperty.RegisterAttached("ConstrainedWidth", typeof(double), typeof(ConstrainWidthHeight), new PropertyMetadata(double.NaN, OnConstrainValuesChanged)); 
     public static readonly DependencyProperty ConstrainedHeightProperty = 
      DependencyProperty.RegisterAttached("ConstrainedHeight", typeof(double), typeof(ConstrainWidthHeight), new UIPropertyMetadata(double.NaN, OnConstrainValuesChanged)); 

     public static double GetConstrainedHeight(FrameworkElement obj) { 
      return (double) obj.GetValue(ConstrainedHeightProperty); 
     } 

     public static void SetConstrainedHeight(FrameworkElement obj, double value) { 
      obj.SetValue(ConstrainedHeightProperty, value); 
     } 

     public static double GetConstrainedWidth(FrameworkElement obj) { 
      return (double) obj.GetValue(ConstrainedWidthProperty); 
     } 

     public static void SetConstrainedWidth(FrameworkElement obj, double value) { 
      obj.SetValue(ConstrainedWidthProperty, value); 
     } 

     private static void OnConstrainValuesChanged(object sender, DependencyPropertyChangedEventArgs e) { 
      FrameworkElement element = sender as FrameworkElement; 
      if(element != null) { 
       double width = GetConstrainedWidth(element); 
       double height = GetConstrainedHeight(element); 

       if(width != double.NaN && height != double.NaN) { 
        double value = Math.Min(width, height); 

        element.Width = value; 
        element.Height = value; 
       } 
      } 
     } 
    } 
} 

rồi, bây giờ lý do tại sao bằng cách sử dụng một hành vi gắn được yêu cầu (AFAICT anyway), là nhằm tập trung các hình elip (trong một trường hợp không vuông/không phải vòng tròn), bạn cần HorizontalAlignment và VerticalAlignment để có thể có hiệu lực. Giá trị mặc định của cả hai là Stretch và khi một Chiều rộng/Chiều cao rõ ràng được đặt, nó hoạt động như Trung tâm.

Với Stretch = "Uniform" bật, Ellipse của bạn sẽ luôn luôn chiếm lĩnh toàn bộ không gian, nó chỉ là bản vẽ của Ellipse sẽ bị hạn chế. Sử dụng hình này, hình Ellipse được vẽ của bạn sẽ luôn bắt đầu ở trên cùng bên trái. Vì vậy, trong trường hợp này nếu nút của bạn là Rộng hơn là cao, phần được vẽ của Ellipse sẽ không được căn giữa cùng với văn bản.

Mã này là một ví dụ tốt về những gì bạn đang có lẽ không tìm kiếm

<Ellipse Height="{TemplateBinding ActualHeight}" Width="{TemplateBinding ActualWidth}" Fill="LightGreen" Stretch="Uniform" /> 

... và nút sử dụng nó (có chiều rộng không vuông/chiều cao):

<Button Content="YO!" Style="{StaticResource Button2}" Width="120" Height="53" VerticalAlignment="Top"></Button> 

Trông như thế này:

Ugly http://www.freeimagehosting.net/uploads/84e62c4982.png

...so với này với tùy chọn tài sản gắn liền:

alt text http://www.freeimagehosting.net/uploads/40755babcd.png

+0

ok tôi đã thử phương pháp này và có vẻ như áp dụng HorizontalAlignment = "Trung tâm" VerticalAlignment = "Trung tâm" để Ellipse cũng làm cho hình dạng vòng tròn biến mất sau khi thay đổi kích thước để một lớn kích thước. Tôi tự hỏi chuyện gì đang xảy ra. – icelava

+0

Đã cập nhật câu trả lời của tôi để sử dụng ActualWidth và ActualHeight. Ràng buộc để W/H là lý do tại sao nó đang làm những gì bạn đang nhìn thấy. –

+0

Sau khi chuyển sang ActualWidth và ActualHeight, nó xuất hiện để hiển thị ở trung tâm ngay bây giờ. Mặc dù tôi vẫn chưa hiểu hết tại sao vậy, cảm ơn. – icelava

0

Set Stretch = Uniform trên Ellipse sẽ tự động hoạt động như Min (Chiều cao, Width) Bạn không cần một tài sản gắn liền như như Adam gợi ý.

<Style x:Key="Button2" TargetType="Button"> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="Button"> 
        <Grid> 
         <Ellipse Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" Fill="LightGreen" Stretch="Uniform"/> 
         <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> 
        </Grid> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
+0

Định dạng này hoạt động nhưng khi tôi thêm HorizontalAlignment = "Center" VerticalAlignment = "Center" vào phần tử Ellipse để tập trung nó với ContentPresenter, nó biến mất. Bất kỳ ý tưởng? – icelava

1

Chỉ cần tự mình điều hành.

Tôi đã có đối tượng hình elip có stretch=uniform để lấp đầy lưới, nhưng không bị giới hạn. Điều này hoạt động ok, tuy nhiên, vòng tròn sẽ không được tập trung trong trường hợp lưới điện không hoàn toàn vuông.

Đặt elip trong một viewbox giải quyết vấn đề này cho tôi:

<Grid> 
    <Viewbox> 
     <Ellipse Stretch="Uniform"/> 
    </Viewbox> 
</Grid> 

Vấn đề duy nhất ở đây là kích thước cơ sở của elip là 0 (hoặc 1, không chắc chắn), có nghĩa là sử dụng một stroke sẽ tự động điền toàn bộ hình elip (nét vẽ sẽ được đặt trên hình elip 0-kích thước, và sau đó hình elip sẽ được thay đổi kích cỡ).

Với một cơn đột quỵ:

Có thể chỉnh lại các cơn đột quỵ không làm việc, là sử dụng một minheight/MinWidth cho elip:

<Grid Background="Yellow"> 
    <Viewbox> 
     <Ellipse Style="{Binding Path=StyleStatus, ConverterParameter='CoilEllipse', Converter={StaticResource styleConverter}}"/> 
    </Viewbox> 
</Grid> 

Sử dụng phong cách sau đây:

<Style x:Key="BaseCoilEllipse" TargetType="Ellipse"> 
    <Setter Property="MinHeight" Value="10" /> 
    <Setter Property="MinWidth" Value="10" /> 
    <!-- Because the ellipse is inside a viewbox, the stroke will be dependant on MinHight/MinWidth. 
     Basically the stroke will now be 4/10th of the ellipse (strokethickness*2/MinHeight/MinWidth) --> 
    <Setter Property="StrokeThickness" Value="2"/> 
    <Setter Property="Stroke" Value="Green" /> 

    <Setter Property="Stretch" Value="Uniform" /> 
    <Setter Property="Fill" Value="Black"/> 
</Style> 

(Lưu ý rằng StrokeThickness bây giờ là tương đối)

Kết quả:

Centered ellipse with stroke

Các vấn đề liên quan