[英]WPF user controls and name scoping
我一直在玩WPF和MVVM,並注意到一件奇怪的事情。 在自定義用戶控件上使用{Binding ElementName=...}
,用戶控件中的根元素的名稱似乎在使用該控件的窗口中可見。 說,這是一個示例用戶控件:
<UserControl x:Class="TryWPF.EmployeeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TryWPF"
Name="root">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding}"/>
<Button Grid.Column="1" Content="Delete"
Command="{Binding DeleteEmployee, ElementName=root}"
CommandParameter="{Binding}"/>
</Grid>
</UserControl>
看起來非常合法。 現在,依賴屬性DeleteEmployee
在代碼隱藏中定義,如下所示:
public partial class EmployeeControl : UserControl
{
public static DependencyProperty DeleteEmployeeProperty
= DependencyProperty.Register("DeleteEmployee",
typeof(ICommand),
typeof(EmployeeControl));
public EmployeeControl()
{
InitializeComponent();
}
public ICommand DeleteEmployee
{
get
{
return (ICommand)GetValue(DeleteEmployeeProperty);
}
set
{
SetValue(DeleteEmployeeProperty, value);
}
}
}
這里沒什么神秘的。 然后,使用控件的窗口如下所示:
<Window x:Class="TryWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TryWPF"
Name="root"
Title="Try WPF!" Height="350" Width="525">
<StackPanel>
<ListBox ItemsSource="{Binding Employees}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<local:EmployeeControl
HorizontalAlignment="Stretch"
DeleteEmployee="{Binding DataContext.DeleteEmployee, ElementName=root}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
沒有什么花哨......除了窗口和用戶控件都具有相同名稱的事實! 但是我希望root
在整個窗口XAML文件中意味着相同的事情,因此請參考窗口,而不是用戶控件。 唉,運行時會打印以下消息:
System.Windows.Data錯誤:40:BindingExpression路徑錯誤:'object'''String'(HashCode = -843597893)'上找不到'DeleteEmployee'屬性。 BindingExpression:路徑= DataContext.DeleteEmployee; DataItem ='EmployeeControl'(Name ='root'); target元素是'EmployeeControl'(Name ='root'); target屬性是'DeleteEmployee'(類型'ICommand')
DataItem='EmployeeControl' (Name='root')
讓我認為它將ElementName=root
視為引用控件本身。 它在string
上查找DeleteEmployee
的事實證實了這種懷疑,因為string
正是我設計的VM中的數據上下文。 這是為了完整性:
class ViewModel
{
public ObservableCollection<string> Employees { get; private set; }
public ICommand DeleteEmployee { get; private set; }
public ViewModel()
{
Employees = new ObservableCollection<string>();
Employees.Add("e1");
Employees.Add("e2");
Employees.Add("e3");
DeleteEmployee = new DelegateCommand<string>(OnDeleteEmployee);
}
private void OnDeleteEmployee(string employee)
{
Employees.Remove(employee);
}
}
它被實例化並分配給構造函數中的窗口,這是窗口代碼隱藏的唯一內容:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
這種現象提示以下問題:
Name
不應該在自定義控件中使用? FindAncestor
模式下使用{RelativeSource}
,這種方法運行正常,但是有更好的方法嗎? 在這種情況下,你對wpf名稱范圍如何工作的困惑是不可思議的 。
您的問題只是您正在對UserControl應用綁定,UserControl是其自己的名稱范圍的“根”(可以這么說)。 UserControls和幾乎所有容器對象都有自己的名稱范圍。 這些范圍不僅包含子元素,還包含包含名稱范圍的對象。 這就是為什么你可以將x:Name="root"
到你的窗口並且(在這種情況下除外)從子控件中找到它。 如果你做不到 ,名望望遠鏡將毫無用處。
當你在一個包圍范圍的范圍內對一個名望鏡的根行動時,會出現混亂。 你的假設是父母的名稱范圍優先,但事實並非如此。 Binding在目標對象上調用FindName,在您的情況下是您的用戶控件 。 (旁注,Binding沒有做插孔,實際調用可以在ElementObjectRef.GetObject
找到,但這是Binding委托調用的地方)
在名稱范圍的根目錄上調用FindName
時,僅檢查在此范圍內定義的名稱。 不搜索父作用域。 (編輯...從第46行開始,更多地閱讀源http://referencesource.microsoft.com/#PresentationFramework/src/Framework/MS/Internal/Data/ObjectRef.cs,5a01adbbb94284c0我看到算法走了直到找到目標,直到可視樹為止,因此子范圍優先於父范圍)
所有這一切的結果是你獲得了用戶控件實例而不是窗口,就像你希望的那樣。 現在,回答你的個人問題......
這是設計的嗎?
是的。 否則名稱范圍不起作用。
2.如果是這樣,那么使用自定義控件的人如何知道它在內部使用的名稱?
理想情況下,你不會。 就像你永遠不希望有知道的根目錄的名稱TextBox
。 有趣的是,在嘗試修改控件的外觀時,了解控件中定義的模板名稱通常很重要...
3.如果Name不應該在自定義控件中使用? 如果是這樣,那么有哪些替代方案呢? 我轉而在FindAncestor模式下使用{RelativeSource},這種方法運行正常,但是有更好的方法嗎?
沒有! 沒關系。 用它。 如果您不與其他人共享UserControl,請確保在遇到此特定問題時更改其名稱。 如果你沒有任何問題,整天重復使用相同的名稱,它不會傷害任何東西。
如果您要共享UserControl ,則應該將其重命名為不會與其他人的名稱沖突的內容。 稱之為MuhUserControlTypeName_MuhRoot_Durr或其他東西。
如果是這樣,那么有哪些替代方案呢? 我轉而在FindAncestor模式下使用{RelativeSource},這種方法運行正常,但是有更好的方法嗎?
羅。 只需更改用戶控件的x:Name
,然后繼續。
5.這是否與數據模板定義自己的名稱相關的事實有關? 如果我只是重命名它,那么它不會阻止我從模板中引用主窗口,因此名稱不會與控件沖突。
不,我不相信。 無論如何,我認為沒有任何理由。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.