동일한 방식으로 지속적으로 재사용되는 연결된 명령 및 논리가있는 컨트롤 집합이 있습니다. 모든 공통 컨트롤과 논리를 포함하는 사용자 컨트롤을 만들기로 결정했습니다.
그러나 이름을 지정할 수있는 콘텐츠를 보유 할 수있는 컨트롤도 필요합니다. 다음을 시도했습니다.
<UserControl.ContentTemplate>
<DataTemplate>
<Button>a reused button</Button>
<ContentPresenter Content="{TemplateBinding Content}"/>
<Button>a reused button</Button>
</DataTemplate>
</UserControl.ContentTemplate>
그러나 사용자 정의 컨트롤 내부에있는 콘텐츠는 이름을 지정할 수 없습니다. 예를 들어 다음과 같은 방식으로 컨트롤을 사용하는 경우 :
<lib:UserControl1>
<Button Name="buttonName">content</Button>
</lib:UserControl1>
다음과 같은 오류가 발생합니다.
‘Button’요소에 이름 속성 값 ‘buttonName’을 설정할 수 없습니다. ‘Button’은 다른 범위에서 정의되었을 때 이미 이름이 등록되어있는 ‘UserControl1’요소의 범위에 있습니다.
buttonName을 제거하면 컴파일되지만 콘텐츠의 이름을 지정할 수 있어야합니다. 이것을 어떻게 할 수 있습니까?
답변
대답은 UserControl을 사용하지 않는 것입니다.
ContentControl 을 확장하는 클래스 만들기
public class MyFunkyControl : ContentControl
{
public static readonly DependencyProperty HeadingProperty =
DependencyProperty.Register("Heading", typeof(string),
typeof(HeadingContainer), new PropertyMetadata(HeadingChanged));
private static void HeadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((HeadingContainer) d).Heading = e.NewValue as string;
}
public string Heading { get; set; }
}
그런 다음 스타일을 사용하여 내용을 지정하십시오.
<Style TargetType="control:MyFunkyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="control:MyFunkyContainer">
<Grid>
<ContentControl Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
그리고 마지막으로-그것을 사용하십시오
<control:MyFunkyControl Heading="Some heading!">
<Label Name="WithAName">Some cool content</Label>
</control:MyFunkyControl>
답변
XAML을 사용하면 불가능한 것 같습니다. 사용자 지정 컨트롤은 실제로 필요한 모든 컨트롤을 가지고 있지만 약간의 논리와 함께 그룹화하고 명명 된 콘텐츠를 허용하면되는 경우 과잉 인 것 같습니다.
mackenir이 제안한대로 JD의 블로그 에있는 솔루션 은 최상의 타협을하는 것 같습니다. XAML에서 컨트롤을 계속 정의 할 수 있도록 JD의 솔루션을 확장하는 방법은 다음과 같습니다.
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
var grid = new Grid();
var content = new ContentPresenter
{
Content = Content
};
var userControl = new UserControlDefinedInXAML();
userControl.aStackPanel.Children.Add(content);
grid.Children.Add(userControl);
Content = grid;
}
위의 예에서는 XAML을 사용하는 일반 사용자 컨트롤과 같이 정의되는 UserControlDefinedInXAML이라는 사용자 컨트롤을 만들었습니다. 내 UserControlDefinedInXAML에는 명명 된 콘텐츠가 표시되기를 원하는 aStackPanel이라는 StackPanel이 있습니다.
답변
내가 사용한 또 다른 대안 Name
은 Loaded
이벤트 에서 속성을 설정하는 것 입니다.
제 경우에는 코드 숨김에서 만들고 싶지 않은 다소 복잡한 컨트롤이 있었고 특정 동작에 대한 특정 이름을 가진 선택적 컨트롤을 찾았습니다. 이벤트에서도 DataTemplate
할 수 있다고 생각했습니다 Loaded
.
private void Button_Loaded(object sender, RoutedEventArgs e)
{
Button b = sender as Button;
b.Name = "buttonName";
}
답변
때로는 C #에서 요소를 참조해야 할 수도 있습니다. 사용 사례에 따라를 x:Uid
대신 설정하고 WPF의 Uid로 Get 개체x:Name
와 같은 Uid 파인더 메서드를 호출하여 요소에 액세스 할 수 있습니다 .
답변
이 도우미를 사용자 컨트롤 내부의 집합 이름에 사용할 수 있습니다.
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
namespace UI.Helpers
{
public class UserControlNameHelper
{
public static string GetName(DependencyObject d)
{
return (string)d.GetValue(UserControlNameHelper.NameProperty);
}
public static void SetName(DependencyObject d, string val)
{
d.SetValue(UserControlNameHelper.NameProperty, val);
}
public static readonly DependencyProperty NameProperty =
DependencyProperty.RegisterAttached("Name",
typeof(string),
typeof(UserControlNameHelper),
new FrameworkPropertyMetadata("",
FrameworkPropertyMetadataOptions.None,
(d, e) =>
{
if (!string.IsNullOrEmpty((string)e.NewValue))
{
string[] names = e.NewValue.ToString().Split(new char[] { ',' });
if (d is FrameworkElement)
{
((FrameworkElement)d).Name = names[0];
Type t = Type.GetType(names[1]);
if (t == null)
return;
var parent = FindVisualParent(d, t);
if (parent == null)
return;
var p = parent.GetType().GetProperty(names[0], BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);
p.SetValue(parent, d, null);
}
}
}));
public static DependencyObject FindVisualParent(DependencyObject child, Type t)
{
// get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
// we’ve reached the end of the tree
if (parentObject == null)
{
var p = ((FrameworkElement)child).Parent;
if (p == null)
return null;
parentObject = p;
}
// check if the parent matches the type we’re looking for
DependencyObject parent = parentObject.GetType() == t ? parentObject : null;
if (parent != null)
{
return parent;
}
else
{
// use recursion to proceed with next level
return FindVisualParent(parentObject, t);
}
}
}
}
창 또는 제어 코드 뒤에 속성으로 제어 할 수 있습니다.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public Button BtnOK { get; set; }
}
귀하의 창 xaml :
<Window x:Class="user_Control_Name.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:test="clr-namespace:user_Control_Name"
xmlns:helper="clr-namespace:UI.Helpers" x:Name="mainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<test:TestUserControl>
<Button helper:UserControlNameHelper.Name="BtnOK,user_Control_Name.MainWindow"/>
</test:TestUserControl>
<TextBlock Text="{Binding ElementName=mainWindow,Path=BtnOK.Name}"/>
</Grid>
</Window>
UserControlNameHelper는 컨트롤을 속성으로 설정하기 위해 컨트롤 이름과 클래스 이름을 가져옵니다.
답변
필요한 각 요소에 대해 추가 속성을 만들기로 선택했습니다.
public FrameworkElement First
{
get
{
if (Controls.Count > 0)
{
return Controls[0];
}
return null;
}
}
이를 통해 XAML의 자식 요소에 액세스 할 수 있습니다.
<TextBlock Text="{Binding First.SelectedItem, ElementName=Taxcode}"/>
답변
<Popup>
<TextBox Loaded="BlahTextBox_Loaded" />
</Popup>
뒤에있는 코드 :
public TextBox BlahTextBox { get; set; }
private void BlahTextBox_Loaded(object sender, RoutedEventArgs e)
{
BlahTextBox = sender as TextBox;
}
실제 해결책은 Microsoft가이 문제를 해결하는 것뿐만 아니라 시각적 트리가 깨진 다른 모든 문제도 해결하는 것입니다.