2010-12-28 54 views
15

Tôi muốn tạo Hộp ngắm (hoặc thứ gì đó tương tự) chỉ có chiều cao của nó và sau đó kéo dài nội dung theo chiều ngang.Tạo quy mô Viewbox theo chiều dọc nhưng kéo ngang theo chiều ngang

Nếu tôi làm điều này:

<Viewbox> 
    <StackPanel> 
    <Button>Foo</Button> 
    <Button>Bar</Button> 
    </StackPanel> 
</Viewbox> 

sau đó tôi có được điều này:

http://www.excastle.com/misc/viewbox-center.png

Nó đóng vai trò như nếu cả hai nút có HorizontalAlignment = "Trung tâm", và sau đó quy mô kết quả . Nhưng tôi không muốn HorizontalAlignment = "Center"; Tôi muốn HorizontalAlignment = "Stretch", như thế này:

http://www.excastle.com/misc/viewbox-stretch.png

Vì vậy, tôi muốn nó đọc chiều cao mong muốn nội dung của nó, tính toán một yếu tố rộng chỉ dựa trên chiều cao, và sau đó cho phép các nội dung quy mô để căng theo chiều ngang.

Có cách nào để thực hiện việc này bằng cách sử dụng Hộp xem và/hoặc một số bảng điều khiển của bên thứ ba không?

Trả lời

16

Không có quyền kiểm soát như vậy bao gồm với WPF nhưng bạn có thể viết một mình mà không gặp quá nhiều rắc rối.Đây là một ViewboxPanel tùy chỉnh mà có thông số kỹ thuật của bạn:

public class ViewboxPanel : Panel 
{ 
    private double scale; 

    protected override Size MeasureOverride(Size availableSize) 
    { 
     double height = 0; 
     Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 
     foreach (UIElement child in Children) 
     { 
      child.Measure(unlimitedSize); 
      height += child.DesiredSize.Height; 
     } 
     scale = availableSize.Height/height; 

     return availableSize; 
    } 

    protected override Size ArrangeOverride(Size finalSize) 
    { 
     Transform scaleTransform = new ScaleTransform(scale, scale); 
     double height = 0; 
     foreach (UIElement child in Children) 
     { 
      child.RenderTransform = scaleTransform; 
      child.Arrange(new Rect(new Point(0, scale * height), new Size(finalSize.Width/scale, child.DesiredSize.Height))); 
      height += child.DesiredSize.Height; 
     } 

     return finalSize; 
    } 
} 

và bạn sử dụng nó như thế này:

<local:ViewboxPanel> 
    <Button>Foo</Button> 
    <Button>Bar</Button> 
</local:ViewboxPanel> 

Nó chắc chắn cần một số công việc nhưng điều này có thể giúp bạn bắt đầu.

+0

Cảm ơn giải pháp tuyệt vời. Tôi đã thêm 'if (System.ComponentModel.DesignerProperties.GetIsInDesignMode (điều này)) { availableSize = new Size (200, 200); } ' trong phương pháp' biện pháp ghi đè kích thước được bảo vệ Kích thướcOverride (Kích thước có sẵnSize) 'để nhà thiết kế hoạt động mà không cần ném ngoại lệ. – Mike

0

Tôi khá chắc chắn câu trả lời là "không dễ dàng".

Dưới đây là một ý kiến ​​cho rằng dường như làm việc nhưng nó loại clunky:

  1. Ném StackPanel của bạn thành một UserControl (UserControl1 trong ví dụ dưới đây).

  2. Đặt hai bản sao của UserControl này vào một vùng chứa (một Lưới trong ví dụ bên dưới).

    a. Căn chỉnh đầu tiên sao chép/trái để nó vẫn ở kích thước mặc định của nó. Bản sao này là đúng cho các mục đích đo lường và nên có Visibility của nó thiết lập để ẩn.

    b. Đặt bản sao thứ hai vào trong Viewbox. Bản sao này là bản sao mà người dùng thực sự sẽ thấy.

  3. Sử dụng MultiBinding với MultiValueConverter để điều chỉnh chiều rộng của UserControl thứ hai sao cho nó có tỷ lệ khung hình chính xác trước khi được mở rộng bởi Hộp nhìn.

Dưới đây là đánh dấu:

<Grid> 
    <local:UserControl1 x:Name="RawControl" HorizontalAlignment="Left" VerticalAlignment="Top" Visibility="Hidden" /> 
    <Viewbox> 
     <local:UserControl1> 
      <local:UserControl1.Width> 
       <MultiBinding Converter="{StaticResource WidthAdjuster}"> 
        <Binding ElementName="RawControl" Path="ActualHeight" /> 
        <Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualWidth" /> 
        <Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualHeight" /> 
       </MultiBinding> 
      </local:UserControl1.Width> 
     </local:UserControl1> 
    </Viewbox> 
</Grid> 

Đây là MultiValueConverter

public class WidthAdjuster : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     var rawHeight = (double)values[0]; 
     var containerWidth = (double)values[1]; 
     var containerHeight = (double)values[2]; 
     var ratio = containerWidth/containerHeight; 
     return rawHeight * ratio; 
    } 

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

Kết quả cho 525 x 350 thùng chứa

alt text

4

Để giữ chiều rộng để làm việc một cách chính xác:

protected override Size MeasureOverride(Size availableSize) 
    { 
     double height = 0; 
     Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 
     foreach (UIElement child in Children) 
     { 
      child.Measure(unlimitedSize); 
      height += child.DesiredSize.Height; 
     } 
     scale = availableSize.Height/height; 

     foreach (UIElement child in Children) 
     { 
      unlimitedSize.Width = availableSize.Width/scale; 
      child.Measure(unlimitedSize); 
     }   

     return availableSize; 
    } 
0

Tôi đã có một vấn đề gần như tương tự. Bảng điều khiển của tôi đã phải phù hợp với tất cả các con của nó, đặt tham trong một hàng và kéo dài chúng, làm đầy bảng điều khiển thống nhất. Thuật toán trên sử dụng biến đổi kết xuất thành các phần tử tỷ lệ. Vấn đề là việc chuyển đổi kết xuất sẽ tự kéo dài trẻ em, nhưng bỏ qua lề. Nếu tỷ lệ ký quỹ cao và hệ số quy mô nhỏ hơn 1, các phần tử rơi ra khỏi bảng. Bạn phải sửa tỷ lệ cho RenderTransform theo lề trên trục đó và sử dụng hệ số quy mô tương tự để sắp xếp. MeasureOverride Tôi đã từng là

protected override Size MeasureOverride(Size availableSize) 
{ 
    double width = 0; double maxHeight = 0; double mY=0; double mX=0; 
    Size unlimitedSize = new Size(double.PositiveInfinity, double.PositiveInfinity); 
    foreach (UIElement child in Children) 
    { 
     child.Measure(unlimitedSize); 
     width += child.DesiredSize.Width; 
     maxHeight = Math.Max(maxHeight, child.DesiredSize.Height); 

     FrameworkElement cld = child as FrameworkElement; 
     mY = cld.Margin.Top + cld.Margin.Bottom; 
     mX = cld.Margin.Left + cld.Margin.Right; 
    } 

    double scaleX = availableSize.Width/width; 
    double scaleY = availableSize.Height/maxHeight; 
    //That is scale for arranging 
    positionScaling = Math.Min(scaleX, scaleY); 

    try 
    { 
     // Let FrameworkElement hight be Xn. mY = Element.Margin.Top + Element.Margin.bottom. 
     // DesiredSize includes margin therefore: 
     // (Yn + mY) * scaleY = availableSize.Height 
     // But render transform doesn't scales margin. Actual render height with margin will be 
     // Yn * RenderScaleY + mY = availableSize.Height; 
     // We must find render transform scale coeff like this: 

     double yn = availableSize.Height/scaleY - mY; 
     scaleY = (availableSize.Height - mY)/yn; 

     double xn = availableSize.Width/scaleX - mX; 
     scaleX = (availableSize.Width - mX)/xn; 


     scale = Math.Min(scaleX, scaleY); //scale to use in RenderTransform 
     // In my project all children are similar in size and margin, algorithm BREAKS otherwise!!! 
    } 
    catch { scale = 1; } 

    return availableSize; 
} 

Một lần nữa: trong dự án của tôi tất cả trẻ em cũng tương tự như kích thước và lề, thuật toán BREAKS khác.

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