2015-03-11 3 views
0

Я создал небольшой Control Browser File:Проблема с DependencyProperty связывания

<UserControl x:Class="Test.UserControls.FileBrowserControl" 
      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="44" d:DesignWidth="461" Name="Control"> 
    <Grid Margin="3"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*"/> 
      <ColumnDefinition Width="Auto"/> 
     </Grid.ColumnDefinitions> 
     <TextBox Margin="3" Text="{Binding SelectedFile}" IsReadOnly="True" TextWrapping="Wrap" /> 
     <Button HorizontalAlignment="Right" Margin="3" Width="100" Content="Browse" Grid.Column="1" Command="{Binding BrowseCommand}" /> 
    </Grid> 
</UserControl> 

С помощью следующего кода позади:

public partial class FileBrowserControl : UserControl 
{ 
    public ICommand BrowseCommand { get; set; } 
    //The dependency property 
    public static DependencyProperty SelectedFileProperty = DependencyProperty.Register("SelectedFile", 
     typeof(string),typeof(FileBrowserControl), new PropertyMetadata(String.Empty)); 
    public string SelectedFile { get{ return (string)GetValue(SelectedFileProperty);} set{ SetValue(SelectedFileProperty, value);}} 
    //For my first test, this is a static string 
    public string Filter { get; set; } 

    public FileBrowserControl() 
    { 
     InitializeComponent(); 
     BrowseCommand = new RelayCommand(Browse); 
     Control.DataContext = this; 
    } 
    private void Browse() 
    { 
     SaveFileDialog dialog = new SaveFileDialog(); 
     if (Filter != null) 
     { 
      dialog.Filter = Filter; 
     } 
     if (dialog.ShowDialog() == true) 
     { 
      SelectedFile = dialog.FileName; 
     } 
    } 
} 

И я использую это так:

<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" Filter="XSLT File (*.xsl)|*.xsl|All Files (*.*)|*.*"/> 

(SelectedFile - свойство ViewModel пользовательского контроля с использованием этого элемента управления)

В настоящее время проблема заключается в том, что когда я нажимаю «Обзор», текстовое поле в usercontrol корректно обновляется, но свойство SelectedFile родительского элемента viewmodel не задано (нет вызова установленного свойства).

Если установить режим связывания с TwoWay, я получил это исключение:

An unhandled exception of type 'System.StackOverflowException' occurred in Unknown Module. 

Так что я сделал не так?

ответ

4

Основная проблема в том, что вы установили DataContext вашего UserControl к себе в своем конструкторе:

DataContext = this; 

Вы не должны делать это, потому что он эффективно предотвращает привязку свойств вашего элемента управления к модели представления.

Вместо этого можно изменить связывание в XAML в UserControl как это:

<TextBox Text="{Binding SelectedFile, 
       RelativeSource={RelativeSource AncestorType=UserControl}}" /> 

Теперь, когда вы используете свой UserControl и написать связывании как

<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" /> 

свойство SelectedFile получает привязан к SelectedFile в вашей модели просмотра, которая должна находиться в DataContext, унаследованном от родительского элемента управления.

+0

Кажется, что намного лучше, спасибо. Вы, кажется, очень хорошо знаете хорошие практики. У меня есть небольшой другой вопрос: в настоящее время этот userControl имеет всю свою логику в коде. это распространено? Потому что это не очень уважает архитектуру MVVM, которую я пытаюсь выполнить. – J4N

+0

UserControl обычно имеет всю свою логику в своем коде, по крайней мере те части логики, которые связаны с просмотром. Однако его код не должен (сильно) модифицировать его модель представления. – Clemens

+0

Хорошо, но, допустим, у меня есть фильтр, InitialDirectory, SelectedFile, ... не могу ли я связать это с ViewModel, который содержит эти поля? – J4N

1

Используя это:

<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" ... 

DataContext В FileBrowserControl в уже установлен в себе, поэтому вы фактически просят связываться с SelectedFile где DataContext является FileBrowserControl, а не родитель ViewModel.

Дайте свое имя для просмотра и используйте вместо него привязку ElementName.

SelectedFile="{Binding DataContext.SelectedFile, ElementName=element}" 
+0

И в моей голове я слышу нереальный звук: «Боже, как» :) Большое спасибо, я не думал, что привязка в родительском объекте по-прежнему относится к DataContext для ребенка. Но я немного потерял. Как все эти сторонние библиотеки делают свой пользовательский контроль, не требуя этого «ElementName»? – J4N

+0

Это хороший вопрос. Я думаю, это потому, что они используют CustomControls, а не UserControls, но я не совсем уверен. Вы должны опубликовать это как новый вопрос. –

+0

Я определенно буду, спасибо – J4N

1

Не ступала DataContext из UserControl внутри UserControl:

ЭТО НЕПРАВИЛЬНО:

this.DataContext = someDataContext; 

, потому что если кто-то будет использовать UserControl, его обычная практика, чтобы установить его DataContext и это противоречит тому, что вы установили ранее.

<my:SomeUserControls DataContext="{Binding SomeDataContext}" /> 

Какой из них будет использоваться? Ну, это зависит ...

То же самое касается свойства Name. Вы не должны установить имя UserControl, как это:

<UserControl x:Class="WpfApplication1.SomeUserControl" Name="MyUserControl1" /> 

, потому что она находится в конфликте с

<my:SomeUserControls Name="SomeOtherName" /> 

РЕШЕНИЕ:
В вашем контроле, просто используйте RelativeSource Mode = FindAncestor:

<TextBox Text="{Binding SelectedFile, RelativeSource={RelativeSource AncestorType="userControls:FileBrowserControl"}" /> 

На ваш вопрос о том, как все эти сторонние элементы управления выполнены: они используют TemplateBindin г. Но TemplateBinding можно использовать только в ControlTemplate.http://www.codeproject.com/Tips/599954/WPF-TemplateBinding-with-ControlTemplate

В UserControl Часть XAML представляет Содержание UserControl, а не ControlTemplate/

Смежные вопросы