2009-10-31 27 views
13

Có cách nào để xác định hoạt ảnh ở đâu đó trong xaml (ví dụ như tài nguyên) một lần và sau đó sử dụng lại nhiều lần không? Tôi có rất nhiều bàn chải độc lập trên các bảng dữ liệu khác nhau mà độc lập cần phải bắt đầu cùng một loại hình động dựa trên một bộ dữ liệu. Bây giờ vì có vẻ như một hoạt ảnh phải xác định một Storyboard.TargetName và Storyboard.TargetProperty. Điều này khá nhiều đánh bại mục đích của reusability. Tôi bằng cách nào đó muốn tuyên bố "sử dụng hình ảnh động này tạo thành tài nguyên nhưng áp dụng nó cho một yếu tố khác thời gian này".xác định hoạt ảnh và trình kích hoạt làm tài nguyên có thể sử dụng lại?

Với tôi điều này có vẻ là một yêu cầu khá cơ bản, quan trọng và cần thiết và tôi ngạc nhiên rằng nó không thẳng về phía trước để hoàn thành. Am i thiếu cái gì ở đây?

Điều tương tự cũng áp dụng cho trình kích hoạt. Giả sử tôi có rất nhiều yếu tố hình ảnh khác nhau mà tất cả đều thể hiện cùng một loại trạng thái bằng cách sử dụng hình động màu. Ví dụ. phai màu xanh lá cây khi "hoạt động" phai sang "màu đỏ" khi "lỗi", vv Sự khác biệt duy nhất giữa hình ảnh là hình dạng/cây hình ảnh của họ, hành vi hoạt hình mong muốn giống nhau, tất cả đều có phần tử ở đâu đó trong cây thị giác của họ một thuộc tính của loại màu. Tôi nghĩ rằng nó không phải là khó để tưởng tượng như thế nào tẻ nhạt nó là để xác định lại các hình ảnh động tương tự và datatrigger đặt hơn và hơn nữa. Mọi nhà phát triển đều ghét điều này. Tôi tuyệt vọng tìm kiếm một giải pháp dễ dàng hơn mà không yêu cầu (hoặc ít nhất là rất ít) mã C# phía sau.

Những gì tôi đã đưa ra cho đến nay là thế này:

Xác định hình ảnh động trong một nguồn tài nguyên Lik này (lặp lại điều này cho tất cả các quốc gia cơ bản mà có, như kích hoạt, năng động, không hoạt động, lỗi):

<ColorAnimationUsingKeyFrames x:Key="deactivatingColorAnimation" 
        Storyboard.TargetProperty="Material.(MaterialGroup.Children)[0].Brush.(SolidColorBrush.Color)"      
        FillBehavior="HoldEnd" RepeatBehavior="Forever" AutoReverse="True"> 
     <ColorAnimationUsingKeyFrames.KeyFrames> 
     <LinearColorKeyFrame KeyTime="00:00:00" Value="Gray"/> 
     <LinearColorKeyFrame KeyTime="00:00:0.25" Value="Gray"/> 
     <LinearColorKeyFrame KeyTime="00:00:0.5" Value="Gray" /> 
     <LinearColorKeyFrame KeyTime="00:00:0.75" Value="Gray" /> 
    </ColorAnimationUsingKeyFrames.KeyFrames> 
</ColorAnimationUsingKeyFrames> 

việc sử dụng nó trong kịch bản trong trigger (lặp lại zillions này lần cho mỗi tiểu bang X mỗi stateviusal differnt, luôn luôn đưa ra một tên mới cho kịch bản):

<DataTrigger Binding="{Binding SubstrateHolder.State}" Value="Deactivating"> 
     <DataTrigger.EnterActions> 
      <BeginStoryboard x:Name="someStateVisualDeactivatingStoryboard"> 
       <Storyboard Storyboard.TargetName="someStateVisual"> 
        <StaticResource ResourceKey="deactivatingColorAnimation" /> 
       </Storyboard> 
      </BeginStoryboard> 
     </DataTrigger.EnterActions> 
     <DataTrigger.ExitActions> 
      <RemoveStoryboard BeginStoryboardName="someStateVisualDeactivatingStoryboard" /> 
     </DataTrigger.ExitActions> 
</DataTrigger> 

Bạn có thể dễ dàng hình dung XAML bloat nhiều đến mức nào tôi phải sao chép và dán liên tục cho tất cả những DataTriggers hàng tỷ.

Sẽ thật tuyệt khi xác định tất cả các trình kích hoạt này một lần và áp dụng cho các hình ảnh trạng thái khác nhau. Làm thế nào là một cái gì đó như thế này được giải quyết trong WPF? Bất kỳ mẹo nào?

Trả lời

1

Dường như không có bất kỳ giải pháp XAML nào tốt cho giải pháp chung này. Cuối cùng tôi đã viết thuộc tính đính kèm của riêng mình để xác định tất cả hành vi hoạt ảnh cho một phần tử đã cho. một cái gì đó như thế này:

<DataTemplate> 
    <!-- ... --> 
    <Rectangle Fill="Gray"> 
    <v:AnimationHelper.Animations> 
     <v:StandardColorStateAnimation TargetColorProperty="(Rectangle.Fill).(SolidColorBrush.Color)" TraggetSateProperty={Binding State} /> 
    </v:AnimationHelper.Animations> 
    </Rectangle> 
<DataTemplate> 

Phần còn lại (tạo ra hình ảnh động, vv) được thực hiện trong codebehind.

+0

Ya biết tôi đã thử một loạt các thứ khác nhau để đưa ra một gợi ý cho điều này và nó chỉ là không thể mà không làm một cái gì đó trong mã. Đây là một giải pháp tốt như tôi nghĩ bạn sẽ nhận được. Hurray cho WPF mở rộng, phải không? :) –

+0

Có thể nên sử dụng API hành vi Silverlight cho việc này. Bạn có đồng ý không? IS có tương thích với chuẩn 3.5 .net framework không? – bitbonk

+0

Không, nó không tương thích, mặc dù chúng được cho là vậy. Thành thật mà nói, tôi nghĩ rằng bạn đã tìm thấy một giải pháp tuyệt vời thúc đẩy kiến ​​trúc WPF một cách rất tự nhiên và không nên tự đoán mình ở đây. –

3

Bạn có thể thử thứ gì đó như thế này không?

  • Gói tất cả mẫu kiểm soát hiện tại của bạn bằng phần tử gốc ẩn, ví dụ: một Border hoặc StackPanel, có hộp giới hạn của nó sẽ bao phủ toàn bộ điều khiển.
  • Tạo mẫu kiểu hoặc mẫu điều khiển cho hộp vô hình này chứa tất cả các trình kích hoạt và hoạt ảnh của bạn.
  • Làm cho hoạt ảnh động động một thuộc tính Màu tùy ý trên hộp vô hình.
  • Trong cây trực quan cho tất cả các điều khiển khác nhau của bạn, hãy liên kết bất kỳ thuộc tính nào bạn muốn tạo hiệu ứng cho thuộc tính Màu trên phần tử gốc ẩn.
0

Tôi nhận thấy vấn đề này hơi chết tại thời điểm đăng bài này, nhưng tôi đã tìm thấy một giải pháp yêu cầu rất ít mã phía sau.

Bạn có thể tạo UserControl with custom properties (Cuộn xuống đến hình 8) có chứa hình chữ nhật của bạn cũng như hoạt ảnh và trình kích hoạt trạng thái. Điều khiển người dùng này sẽ xác định thuộc tính công khai như trạng thái kích hoạt thay đổi màu khi thay đổi.

Chỉ yêu cầu mã duy nhất là tạo biến của bạn bằng mã.

Điều khiển người dùng có thể được sử dụng lại nhiều lần mà không cần viết lại bảng phân cảnh XAML hoặc trình kích hoạt dữ liệu.

0

Nhất "XAML cách" để đạt được mục tiêu này, tôi có thể nghĩ đến là tạo ra dành riêng MarkupExtension đó sẽ kéo các hình ảnh động từ điển nguồn lực và thiết lập các thuộc cần thiết - Tôi giả sử những được giới hạn trong một tập hợp con của Storyboard.Target, Storyboard.TargetNameStoryboard.TargetProperty. Mặc dù nó đòi hỏi một số code-behind, nó là nỗ lực một lần, hơn nữa, MarkupExtension s được thiết kế để được sử dụng với XAML. Dưới đây là phiên bản đơn giản:

[MarkupExtensionReturnType(typeof(Timeline))] 
public class AnimationResourceExtension : StaticResourceExtension 
{ 
    //This property is for convienience so that we 
    //don't have to remember to set x:Shared="False" 
    //on all animation resources, but have an option 
    //to avoid redundant cloning if it is 
    public bool IsShared { get; set; } = true; 

    public DependencyObject Target { get; set; } 

    public string TargetName { get; set; } 

    public PropertyPath TargetProperty { get; set; } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     if (base.ProvideValue(serviceProvider) is Timeline animation) 
     { 
      //If the animation is shared we shall clone it 
      //Checking if it is frozen is optional and we can 
      //either clone it or throw an exception 
      //(or simply proceed knowing one will be thrown anyway) 
      if (IsShared || animation.IsFrozen) 
       animation = animation.Clone(); 
      Storyboard.SetTarget(animation, Target); 
      Storyboard.SetTargetName(animation, TargetName); 
      Storyboard.SetTargetProperty(animation, TargetProperty); 
      return animation; 
     } 
     else 
      throw new XamlException("The referenced resource is not an animation"); 
    } 
} 

Cách sử dụng rất đơn giản:

<FrameworkElement.Resources> 
    <DoubleAnimation x:Key="MyAnimation" From="0" To="1" Duration="0:0:1" /> 
</FrameworkElement.Resources> 
(...) 
<Storyboard> 
    <utils:AnimationResource ResourceKey="MyAnimation" TargetName="SomeElement" TargetProperty="Opacity" /> 
</Storyboard> 

Là đơn giản như nó có thể là giải pháp này có những hạn chế của nó - nó không hỗ trợ không Binding cũng không DynamicResource phần mở rộng cho các thuộc tính nói trên. Tuy nhiên điều này là có thể đạt được, nhưng đòi hỏi một số nỗ lực thêm. Binding hỗ trợ nên khá đơn giản - một câu hỏi về việc sử dụng thích hợp XamlSetMarkupExtensionAttribute (cộng với một số mã soạn sẵn). DynamicResource hỗ trợ sẽ phức tạp hơn một chút, và ngoài việc sử dụng XamlSetMarkupExtensionAttribute sẽ yêu cầu gói IServiceProvider để trả lại việc thực hiện đầy đủ IProvideValueTarget, nhưng vẫn có thể.

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