我想引用另一个元素,它嵌套在自定义用户控件下:
<userControls:Test>
<TextBox Name="Foo" />
<Button CommandParameter="{Binding Text, ElementName=Foo" Command="{Binding anything}" />
</userControls:Test>
基本上我正在使用命令而我正在尝试引用另一个带有名称的元素,但我得到了这个名字:
无法在元素'...'上设置名称属性值'...'。 '...' 在下面 元素'...'的范围,已经注册了名称 它是在另一个范围内定义的。
我认为命名元素是不可能的:How to create a WPF UserControl with NAMED content
那么有没有其他方式来引用嵌套自定义用户控件内容中的元素?
答案 0 :(得分:3)
不是您问题的直接答案,而是合理的解决方法。在您的视图中,将文本绑定到viewmodel上的FooText属性,直接在ICommand的声明旁边。执行命令时,使用最新的FooText
<!-- View -->
<userControls:Test>
<TextBox Text={Binding FooText, Mode=TwoWay} />
<Button Command="{Binding FooCommand}" />
</userControls:Test>
// ViewModel
public string FooText { get; set; }
public ICommand FooCommand
{
get
{
return new DelegateCommand(() => ExecuteFoo(FooText));
}
}
答案 1 :(得分:1)
为什么不为UserControl制作模板。
<....Resources>
<Style TargetType="{x:Type UserControl}" x:Key="MyUserControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<TextBox x:Name="PART_Foo"/>
<TextBlock Text="{Binding ElementName=PART_Foo, Path=Text}"/>
<ContentPresenter Content="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Content}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</....Resources>
<UserControl Style="{StaticResource MyUserControl}">
</UserControl>
然后使用键命名模板,并指定哪个UserControl使用哪个模板。
此外,在您的代码中,您可以使用以下方式获取模板化部件的PART_Name:
UIElement GetUserControlPart(UserControl control, String name)
{
return control.Template.FindName(name, control) as UIElement;
}
答案 2 :(得分:1)
我最终这样做了:
<UserControl x:Class="MyProject.FooBar"
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:userControls="clr-namespace:MyProject.UserControls"
Loaded="Loaded"> <!-- notice -->
<userControls:Test>
<TextBox Initialized="FooInitialized" />
<Button Initialized="AnotherInitialized" />
</userControls:Test>
代码:
// This for every initialization, all we do is set names after the elements are initialized.
private void FooInitialized(object sender, EventArgs e)
{
((TextBox) sender).Name = "Foo";
}
// Here when the entire junk is loaded, we set the necessary commands.
private void Loaded(object sender, EventArgs e)
{
// Find elements.
var button = UIHelper.FindChild<Button>(this, "Bar");
var textBox = UIHelper.FindChild<TextBox>(this, "Foo");
// Set up bindings for button.
button.Command = ((FooViewModel) DataContext).FooCommand;
button.CommandParameter = textBox;
}
UIHelper
来自https://stackoverflow.com/a/1759923/283055。该按钮的名称与文本框的名称相同。
基本上所有名称都是通过Initialized
事件设置的。唯一的缺点是你必须引用代码中的名称,这就是我在整个组件加载后通过代码设置命令的原因。我发现这是代码和UI标记之间的最佳折衷方案。
答案 3 :(得分:0)
如果您使用.NET 4.0并且需要将绑定源设置为兄弟控件,那么您可以使用此MarkupExtension:
public class SiblingSourceBinding : MarkupExtension
{
public Binding Binding
{
get;
set;
}
public int SiblingIndex
{
get;
set;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (provideValueTarget != null)
this.Binding.Source = LogicalTreeHelper.GetChildren(LogicalTreeHelper.GetParent(provideValueTarget.TargetObject as DependencyObject)).Cast<object>().ElementAt(this.SiblingIndex);
else
throw new InvalidOperationException("Unable to set sibling binding source.");
return this.Binding.ProvideValue(serviceProvider);
}
并像这样使用它:
<userControls:Test>
<TextBox/>
<Button CommandParameter="{local:SiblingSourceBinding Binding={Binding Text}, SiblingIndex=0}" Command="{Binding anything}"/>
</userControls:Test>
答案 4 :(得分:0)
您可以在嵌套元素中使用Uid,如下所示:
<userControls:Test Name="usrCtrl">
<TextBox Uid="Foo" />
<Button Uid="btn"/>
</userControls:Test>
然后,您使用以下功能(该功能是此答案Get object by its Uid in WPF的解决方案):
private static UIElement FindUid(this DependencyObject parent, string uid)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count == 0) return null;
for (int i = 0; i < count; i++)
{
var el = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (el == null) continue;
if (el.Uid == uid) return el;
el = el.FindUid(uid);
if (el != null) return el;
}
return null;
}
然后在您的Loaded函数中添加以下内容:
private void Loaded(object sender, EventArgs e)
{
// Find elements.
var button = FindUid(usrCtrl, "btn") as Button;
var textBox = FindUid(usrCtrl, "Foo") as TextBox;
// Do binding here...
}