2016-10-06 20 views
10

Ở nơi làm việc, tôi có nhiều trang, mỗi trang có các nút ở cùng một vị trí và với cùng các thuộc tính. Mỗi trang cũng có những khác biệt nhỏ. Để kết thúc, chúng tôi tạo ra một userControl Template và đặt tất cả các nút trong đó, sau đó áp dụng điều khiển người dùng đó cho tất cả các trang. Tuy nhiên, bây giờ nó là khá khó khăn để truy cập các nút và sửa đổi chúng từ xaml của mỗi trang, bởi vì họ đang ở trong một UserControl trên trang ..... Làm thế nào để tôi truy cập trang nhã các nút từ mỗi trang?Làm cách nào để truy cập các nút bên trong UserControl từ xaml?

Những gì tôi đã cố gắng:

  1. Hiện nay, chúng tôi liên kết với một loạt các thuộc tính phụ thuộc. Tôi không thích tùy chọn này vì tôi có nhiều nút và cần phải kiểm soát nhiều thuộc tính trên các nút đó. Kết quả là hàng trăm thuộc tính phụ thuộc, và một mớ hỗn độn thực sự để lội qua khi chúng ta cần phải thay đổi một cái gì đó.

  2. Phương pháp khác là sử dụng kiểu. Tôi thích phương pháp này nói chung, nhưng vì các nút này nằm bên trong một điều khiển khác nên việc sửa đổi chúng trở nên khó khăn và mẫu sẽ chỉ chính xác cho một nút, cùng một lúc.

  3. Adam Kemp đăng về việc cho phép người dùng chỉ cần chèn nút riêng của họ here và đây là phương pháp tôi hiện đang cố gắng cấy ghép/sửa đổi. Thật không may, tôi không có quyền truy cập vào Xamarin.

Mặc dù mẫu chèn khi mã chạy, mẫu không cập nhật nút một cách chính xác. Nếu tôi đặt điểm ngắt trong MyButton Setter, tôi có thể thấy rằng giá trị thực sự là một nút trống, thay vì nút tôi đã chỉ định trong cửa sổ chính của mình. Làm thế nào để sửa lỗi này?

Dưới đây là một số luật đơn giản:

My Template UserControl của XAML:

<UserControl x:Class="TemplateCode.Template" 
    x:Name="TemplatePage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d" 
    d:DesignHeight="350" 
    d:DesignWidth="525" 
    DataContext="{Binding RelativeSource={RelativeSource Self}}" 
    Background="DarkGray"> 

    <Grid> 
      <Button x:Name="_button" Width="200" Height="100" Content="Template Button"/> 
    </Grid> 
</UserControl> 

My Template UserControl của Mã Đằng sau:

using System.Windows.Controls; 

namespace TemplateCode 
{ 
    public partial class Template : UserControl 
    { 
     public static Button DefaultButton; 

     public Template() 
     { 
      InitializeComponent(); 
     } 

     public Button MyButton 
     { 
      get 
      { 
       return _button; 
      } 
      set 
      { 
       _button = value; //I get here, but value is a blank button?! 

       // Eventually, I'd like to do something like: 
       // Foreach (property in value) 
       // { 
       //  If(value.property != DefaultButton.property)) 
       //  { 
       //   _button.property = value.property; 
       //  } 
       // } 
       // This way users only have to update some of the properties 
      } 
     } 
    } 
} 

Và bây giờ các ứng dụng mà tôi muốn sử dụng nó:

<Window x:Class="TemplateCode.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d" 

    xmlns:templateCode="clr-namespace:TemplateCode" 

    Title="MainWindow" 
    Height="350" 
    Width="525" 
    Background="LimeGreen" 
    DataContext="{Binding RelativeSource={RelativeSource Self}}" > 

    <Grid> 
     <templateCode:Template> 
      <templateCode:Template.MyButton> 

       <Button Background="Yellow" 
        Content="Actual Button" 
        Width="200" 
        Height="100"/> 

      </templateCode:Template.MyButton> 
     </templateCode:Template> 
    </Grid> 
</Window> 

Và Bây giờ Mã Đằng sau:

Using System.Windows; 
Namespace TemplateCode 
{ 
    Public partial class MainWindow : Window 
    { 
     Public MainWindow() 
     { 
      InitializeComponent(); 
     } 
    } 
} 

Edit: Trong khi tôi muốn loại bỏ các thuộc tính phụ thuộc không cần thiết trong mẫu UserControl, tôi vẫn muốn thiết lập các ràng buộc trên thuộc tính nút 's từ XAML.

+0

Bạn cũng có thể tạo một ObservableCollection và đăng ký nó dưới dạng DependencyProperty. Sau đó truy cập nó bằng một bộ chỉ mục. Nếu không, hãy thử giải pháp với một kiểu, http://stackoverflow.com/questions/40015997/c-sharp-wpf-style-control-in-usercontrol –

+0

"Vì các nút này nằm trong một điều khiển khác nên sẽ khó sửa đổi chúng" : Bạn có thể làm rõ? Một thuộc tính cho mỗi nút và người tiêu dùng có toàn quyền kiểm soát. Nếu bạn muốn chia sẻ kiểu, trong khi sử dụng các mẫu khác nhau cho mỗi nút, bạn cũng có thể có thuộc tính mẫu cho mỗi nút. Hoặc chỉ cần sử dụng Style.BasedOn rất nhiều. –

Trả lời

0

Lựa chọn khác dựa trên @ câu trả lời Funk là để thực hiện một kiểm soát nội dung thay vì một nút trên mẫu, sau đó liên kết nội dung kiểm soát nội dung để ButtonProperty của bạn trong các mã sau:

trên mẫu:

<ContentControl Content={Binding myButton} Width="200" Height="100"/> 

trong mẫu mã sau:

public static readonly DependencyProperty myButtonProperty = 
     DependencyProperty.Register("Button", typeof(Button), typeof(Template), 
      new UIPropertyMetadata(new PropertyChangedCallback(ButtonChangedCallback))); 

và sau đó trên cửa sổ chính:

<Window.Resources> 
    <Button x:Key="UserButton" 
      Background="Yellow" 
      Content="Actual Button" 
      /> 
</Window.Resources> 
<Grid> 
    <templateCode:Template myButton="{StaticResource UserButton}"/> 
</Grid> 

Điều thú vị về điều này là Visual Studio đủ thông minh để hiển thị mã này vào thời gian thiết kế, cũng như có ít mã tổng thể hơn.

Bạn có thể đặt mọi thứ liên tục (như vị trí, phông chữ và màu) cho nút của bạn trên điều khiển nội dung hoặc theo kiểu mặc định và sau đó chỉ sửa đổi các phần bạn cần cho bạn.

1

Nếu bạn có thể đổi nhóm của bạn để nút của bạn với một hoặc nhiều thuộc tính trên DataContext của bạn, bạn có thể làm việc với DataTriggers:

<Button x:Name="TestButton"> 
    <Button.Style> 
     <Style> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding IsButtonEnabled}" Value="True"> 
        <Setter TargetName="TestButton" Property="Background" Value="Red" /> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </Button.Style> 
</Button> 

Bạn thậm chí có thể sử dụng nhiều điều kiện với MultiDataTriggers.

+0

Cảm ơn bạn đã trả lời @lhildebrandt! Ràng buộc một loạt các thuộc tính cho một vài biến là một ý tưởng thú vị. Trong trường hợp này, về cơ bản tôi muốn thiết lập nó để nếu có ai đó đặt thuộc tính * any *, họ có thể, mà không làm phiền việc tạo thuộc tính phụ thuộc mới, vì vậy tôi có lẽ sẽ không thể nhóm chúng lại được. Nhưng cảm ơn bạn đã hiển thị tùy chọn kiểu! – bwall

-1

Một lựa chọn là chỉ đơn giản là bắt đầu viết C# trên trang XAML sử dụng < [CDATA [***]]>

Trong Window.xaml chính bạn thay đổi để:

<templateCode:Template x:Name="test"> 
    <x:Code><![CDATA[ 
     Void OnStartup() 
     { 
      test.MyButton.Content="Actual Button"; 
      test.MyButton.Background = new SolidColorBrush(Color.FromArgb(255,255,255,0)); 
     } 
     ]]> 
    </x:Code> 

Sau đó, ngay sau khi khởi tạo Object() bạn gọi OnStartup().

Mặc dù điều này cho phép bạn chỉnh sửa các thuộc tính cụ thể trong xaml, điều này cũng giống như chỉ viết mã trong mã phía sau, nơi những người khác mong đợi nó được.

3

Bạn có thể đăng ký Thuộc tính phụ thuộc Button trên số UserControl và xử lý việc khởi tạo trong số PropertyChangedCallback.

Template.xaml.cs

using System; 
using System.Windows; 
using System.Windows.Controls; 
using System.Collections.Generic; 
using System.Windows.Markup.Primitives; 

namespace TemplateCode 
{ 
    public partial class Template : UserControl 
    { 
     public Template() 
     { 
      InitializeComponent(); 
     } 

     public static readonly DependencyProperty ButtonProperty = 
      DependencyProperty.Register("Button", typeof(Button), typeof(Template), 
       new UIPropertyMetadata(new PropertyChangedCallback(ButtonChangedCallback))); 

     public Button Button 
     { 
      get { return (Button)GetValue(ButtonProperty); } 
      set { SetValue(ButtonProperty, value); } 
     } 

     public static List<DependencyProperty> GetDependencyProperties(Object element) 
     { 
      List<DependencyProperty> properties = new List<DependencyProperty>(); 
      MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(element); 
      if (markupObject != null) 
      { 
       foreach (MarkupProperty mp in markupObject.Properties) 
       { 
        if (mp.DependencyProperty != null) 
        { 
         properties.Add(mp.DependencyProperty); 
        } 
       } 
      } 
      return properties; 
     } 

     private static void ButtonChangedCallback(object sender, DependencyPropertyChangedEventArgs args) 
     { 
      // Get button defined by user in MainWindow 
      Button userButton  = (Button)args.NewValue; 
      // Get template button in UserControl 
      UserControl template = (UserControl)sender; 
      Button templateButton = (Button)template.FindName("button"); 
      // Get userButton props and change templateButton accordingly 
      List<DependencyProperty> properties = GetDependencyProperties(userButton); 
      foreach(DependencyProperty property in properties) 
      { 
       if (templateButton.GetValue(property) != userButton.GetValue(property)) 
       { 
        templateButton.SetValue(property, userButton.GetValue(property)); 
       } 
      } 
     } 
    } 
} 

Template.xaml

UserControl DataContext được thừa hưởng từ cha mẹ, không cần không để thiết lập nó một cách rõ ràng

<UserControl x:Class="TemplateCode.Template" 
    x:Name="TemplatePage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d" 
    d:DesignHeight="350" 
    d:DesignWidth="525" 
    Background="DarkGray"> 

    <Grid> 
     <Button x:Name="button" Width="200" Height="100" Content="Template Button"/> 
    </Grid> 
</UserControl> 

MainWindow .xaml

Bạn đã được thiết lập Button.Content thay vì Button

<Window x:Class="TemplateCode.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d" 

    xmlns:templateCode="clr-namespace:TemplateCode" 

    Title="MainWindow" 
    Height="350" 
    Width="525"> 
    <Window.Resources> 
     <Button x:Key="UserButton" 
       Background="Yellow" 
       Content="Actual Button" 
       Width="200" 
       Height="100" 
       /> 
    </Window.Resources> 
    <Grid> 
     <templateCode:Template Button="{StaticResource UserButton}"/> 
    </Grid> 
</Window> 

EDIT - Binding Button.Content

3 cách để làm điều này:

1. Thuộc tính

Đến nay là phương pháp tốt nhất. Việc tạo ra UserControl DP cho mọi thuộc tính trên Button chắc chắn là quá mức cần thiết, nhưng đối với những người bạn muốn ràng buộc với ViewModel/MainWindow DataContext nó có ý nghĩa.

Thêm vào Template.xaml.cs

public static readonly DependencyProperty TextProperty = 
    DependencyProperty.Register("Text", typeof(string), typeof(Template)); 

public string Text 
{ 
    get { return (string)GetValue(TextProperty); } 
    set { SetValue(TextProperty, value); } 
} 

Template.xaml

<UserControl x:Class="TemplateCode.Template" 

    ... 

    DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
    <Grid> 
     <Button x:Name="button" Width="200" Height="100" Content="{Binding Text}"/> 
    </Grid> 
</UserControl> 

MainWindow.xaml

<Window.Resources> 
    <Button x:Key="UserButton" 
      Background="Yellow" 
      Width="200" 
      Height="100" 
      /> 
</Window.Resources> 
<Grid> 
    <templateCode:Template 
     Button="{StaticResource UserButton}" 
     Text="{Binding DataContext.Txt, 
         RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/> 

</Grid> 

Hoặc

<Window.Resources> 
    <Button x:Key="UserButton" 
      Background="Yellow" 
      Content="Actual Button" 
      Width="200" 
      Height="100" 
      /> 
</Window.Resources> 
<Grid> 
    <templateCode:Template 
     Button="{StaticResource UserButton}"/> 
</Grid> 

Giá trị ưu tiên: UserButton nội dung> DP Text, vì vậy thiết lập các nội dung trong Resources thắng.

2. Tạo Button trong ViewModel

chủ nghĩa thuần túy MVVM của bạn sẽ không như thế này, nhưng bạn có thể sử dụng nhãn hiệu Binding lên thay vì StaticResource.

MainWindow.xaml

<Grid> 
    <templateCode:Template 
     Button="{Binding DataContext.UserButton, 
         RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/> 
</Grid> 

3. Thiết lập các ràng buộc trong mã

Như bạn đã thấy, một chỗ dựa ViewModel (ví dụ Txt) không thể được tham chiếu trong Resources vì thứ tự mọi thứ được khởi tạo. Bạn vẫn có thể làm điều đó trong mã sau, nhưng nó hơi lộn xộn với lỗi để chứng minh.

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=DataContext.Txt; DataItem=null; target element is 'Button' (Name=''); target property is 'Content' (type 'Object')

Lưu ý bạn cần xác định đường dẫn đầy đủ trên Content tài sản (thiết DataContext vào cha mẹ sẽ không làm).

MainWindow.xaml

<Window.Resources> 
    <Button x:Key="UserButton" 
      Background="Yellow" 
      Width="200" 
      Height="100" 
      Content="{Binding DataContext.Txt, 
           RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 
      /> 
</Window.Resources> 
<Grid> 
    <templateCode:Template Button="{StaticResource UserButton}"/> 
</Grid> 

Template.xaml.cs

private static void ButtonChangedCallback(object sender, DependencyPropertyChangedEventArgs args) 
{ 
    // Get button defined by user in MainWindow 
    Button userButton = (Button)args.NewValue; 
    // Get template button in UserControl 
    UserControl template = (UserControl)sender; 
    Button templateButton = (Button)template.FindName("button"); 
    // Get userButton props and change templateButton accordingly 
    List<DependencyProperty> properties = GetDependencyProperties(userButton); 
    foreach (DependencyProperty property in properties) 
    { 
    if (templateButton.GetValue(property) != userButton.GetValue(property)) 
     templateButton.SetValue(property, userButton.GetValue(property)); 
    } 
    // Set Content binding 
    BindingExpression bindingExpression = userButton.GetBindingExpression(Button.ContentProperty); 
    if (bindingExpression != null) 
     templateButton.SetBinding(Button.ContentProperty, bindingExpression.ParentBinding); 
} 
+0

Có thể đặt các ràng buộc trên nút trong phần Windows.Resources không? Tôi chơi xung quanh với nó một chút, nhưng khi tôi cố gắng để ràng buộc các nội dung, nó chỉ cho thấy một nút màu vàng trống. Điều này có vẻ thực sự tốt mặc dù. – bwall

+0

Tôi đã thử chèn 'templateButton.GetBinding (thuộc tính, GetBinding (userButton, property) .ParentBinding' vào buttonChangedCallback như được hiển thị [ở đây] (http://stackoverflow.com/questions/978473/wpf-how-to-bind-to- a-lồng nhau-bất động sản), nhưng điều đó dường như không hoạt động .. – bwall

+0

@bwall Cập nhật câu trả lời của tôi. – Funk

2

chứ không phải sử dụng nhiều tài sản phụ thuộc, thích cách tiếp cận phong cách. Kiểu có chứa mọi thuộc tính có sẵn cho điều khiển Nút.

Tôi sẽ tạo DependencyProperty cho mỗi kiểu nút trong UserControl.

public partial class TemplateUserControl : UserControl 
{ 
    public TemplateUserControl() 
    { 
     InitializeComponent(); 
    } 

    public static readonly DependencyProperty FirstButtonStyleProperty = 
     DependencyProperty.Register("FirstButtonStyle", typeof (Style), typeof (TemplateUserControl)); 

    public Style FirstButtonStyle 
    { 
     get { return (Style)GetValue(FirstButtonStyleProperty); } 
     set { SetValue(FirstButtonStyleProperty, value); } 
    } 

    public static readonly DependencyProperty SecondButtonStyleProperty = 
     DependencyProperty.Register("SecondButtonStyle", typeof (Style), typeof (TemplateUserControl)); 

    public Style SecondButtonStyle 
    { 
     get { return (Style)GetValue(SecondButtonStyleProperty); } 
     set { SetValue(SecondButtonStyleProperty, value); } 
    } 
} 

và sau đó sửa đổi XAML cho các nút bấm để chọn những phong cách:

<UserControl x:Class="MyApp.TemplateUserControl" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="200" d:DesignWidth="300" 
      Background="DarkGray"> 
    <StackPanel> 
     <Button x:Name="_button" Width="200" Height="100" 
       Style="{Binding Path=FirstButtonStyle, RelativeSource={RelativeSource AncestorType=UserControl}}"/> 
     <Button x:Name="_button2" Width="200" Height="100" 
       Style="{Binding Path=SecondButtonStyle, RelativeSource={RelativeSource AncestorType=UserControl}}"/> 
    </StackPanel> 
</UserControl> 

bây giờ khi các nút phải được tùy chỉnh, mà có thể đạt được bằng cách phong cách tùy chỉnh:

<StackPanel> 
    <StackPanel.Resources> 
     <!--common theme properties--> 
     <Style TargetType="Button" x:Key="TemplateButtonBase"> 
      <Setter Property="FontSize" Value="18"/> 
      <Setter Property="Foreground" Value="Blue"/> 
     </Style> 

     <!--unique settings of the 1st button--> 
     <!--uses common base style--> 
     <Style TargetType="Button" x:Key="BFirst" BasedOn="{StaticResource TemplateButtonBase}"> 
      <Setter Property="Content" Value="1st"/> 
     </Style> 

     <Style TargetType="Button" x:Key="BSecond" BasedOn="{StaticResource TemplateButtonBase}"> 
      <Setter Property="Content" Value="2nd"/> 
     </Style> 
    </StackPanel.Resources> 

    <myApp:TemplateUserControl FirstButtonStyle="{StaticResource BFirst}" 
           SecondButtonStyle="{StaticResource BSecond}"/> 
</StackPanel> 

enter image description here

-1

Sử dụng biểu thức lambda hoặc phương thức đại biểu để đặt phương thức cụ thể mỗi nút với mỗi trang

1

Vấn đề chính là các thành phần mẫu được khởi tạo trước các thành phần mainwindow.I có nghĩa là tất cả các thuộc tính của nút trong mainwindow được đặt sau khi nút trong lớp mẫu được khởi tạo. Do đó, như bạn đã nói giá trị đặt thành null. Tất cả những gì tôi muốn nói là về chuỗi các đối tượng khởi tạo. Nếu bạn thực hiện một mẹo như sau;

public partial class Template : UserControl 
{ 
    private Button _btn ; 

    public Template() 
    { 

    } 

    public Button MyButton 
    { 
     get 
     { 
      return _button; 
     } 
     set 
     { 
      _btn = value; 
      _button = value; 
     } 
    } 
    protected override void OnInitialized(EventArgs e) 
    { 
     InitializeComponent(); 
     base.OnInitialized(e); 

     this._button.Content = _btn.Content; 
     this._button.Background = _btn.Background; 
     this.Width = _btn.Width; 
     this.Height = _btn.Height; 
    } 
} 

Nó sẽ hoạt động không chắc chắn.

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