Problema con l’associazione DependencyProperty

Ho creato un piccolo controllo File Browser:

       

Con il seguente codice:

 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; } } } 

E lo uso in questo modo:

  

(SelectedFile è la proprietà del ViewModel di usercontrol che utilizza questo controllo)

Attualmente il problema è che quando faccio clic su Sfoglia, la casella di testo in usercontrol si aggiorna correttamente, ma la proprietà SelectedFile del controllo genitore viewmodel non è impostata (nessuna chiamata alla proprietà set).

Se imposto la modalità del binding su TwoWay, ho ottenuto questa eccezione:

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

Quindi cosa ho fatto di sbagliato?

Il problema principale è che si imposta DataContext di UserControl su se stesso nel suo costruttore:

 DataContext = this; 

Non dovresti farlo, perché infrange qualsiasi Bindings basato su DataContext, ad esempio su un’istanza del modello di vista nel valore DataContext ereditato.

Invece si cambierebbe il binding nell’XAML di UserControl in questo modo:

  

Ora, quando usi UserControl e scrivi un binding come

  

la proprietà SelectedFile viene associata a una proprietà SelectedFile nel modello di visualizzazione, che dovrebbe essere nel DataContext ereditato da un controllo padre.

Usando questo:

  

DataContext di FileBrowserControl è già stato impostato su se stesso, pertanto si sta effettivamente chiedendo di associare a SelectedFile dove DataContext è il FileBrowserControl, non il ViewModel principale.

Assegna un nome alla tua vista e utilizza invece un collegamento ElementName.

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

Non impostare mai DataContext di UserControl all’interno di usercontrol:

QUESTO È SBAGLIATO:

 this.DataContext = someDataContext; 

perché se qualcuno userà il tuo usercontrol, la sua pratica comune è di impostare il suo datacontext ed è in conflitto con ciò che hai impostato in precedenza

  

Quale sarà usato? Beh, dipende…

Lo stesso vale per la proprietà Name. non dovresti impostare il nome in UserControl in questo modo:

  

perché è in conflitto con

  

SOLUZIONE:
Nel tuo controllo, usa la modalità RelativeSource = FindAncestor:

  

Alla tua domanda su come sono fatti tutti questi controlli di terze parti: usano TemplateBinding. Ma TemplateBinding può essere utilizzato solo in ControlTemplate. http://www.codeproject.com/Tips/599954/WPF-TemplateBinding-with-ControlTemplate

In usercontrol xaml rappresenta il contenuto di UserControl, non ControlTemplate /