c# - galasoft - mvvm light uwp
Problema con el enlace DependencyProperty (3)
Creé un pequeño control de explorador de archivos:
<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>
Con el siguiente código detrás:
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;
}
}
}
Y lo uso así:
<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" Filter="XSLT File (*.xsl)|*.xsl|All Files (*.*)|*.*"/>
(SelectedFile es propiedad del ViewModel del usercontrol usando este control)
Actualmente, el problema es que cuando hago clic en Examinar, el cuadro de texto de usercontrol se actualiza correctamente, pero la propiedad SelectedFile del control principal de viewmodel no está configurada (no hay llamadas a la propiedad set).
Si configuro el modo del enlace a TwoWay, obtuve esta excepción:
An unhandled exception of type ''System.StackOverflowException'' occurred in Unknown Module.
Entonces, ¿qué hice mal?
El principal problema es que configura el DataContext de UserControl para sí mismo en su constructor:
DataContext = this;
No debe hacer eso, porque evita de manera efectiva vincular las propiedades de su control a un modelo de vista.
En cambio, cambiaría el enlace en el XAML de UserControl de esta manera:
<TextBox Text="{Binding SelectedFile,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
Ahora, cuando usa UserControl y escribe un enlace como
<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" />
la propiedad SelectedFile se vincula a una propiedad SelectedFile en su modelo de vista, que debe estar en el DataContext heredado de un control principal.
Nunca configure DataContext de UserControl dentro de usercontrol:
ESTO ESTÁ MAL:
this.DataContext = someDataContext;
porque si alguien usa tu usercontrol, es una práctica común establecer su contexto de datos y está en conflicto con lo que has establecido previamente
<my:SomeUserControls DataContext="{Binding SomeDataContext}" />
¿Cuál se usará? Bueno, eso depende...
Lo mismo se aplica a la propiedad Nombre. no debe establecer el nombre para UserControl de esta manera:
<UserControl x:Class="WpfApplication1.SomeUserControl" Name="MyUserControl1" />
porque está en conflicto con
<my:SomeUserControls Name="SomeOtherName" />
SOLUCIÓN:
En su control, simplemente use RelativeSource Mode = FindAncestor:
<TextBox Text="{Binding SelectedFile, RelativeSource={RelativeSource AncestorType="userControls:FileBrowserControl"}" />
A su pregunta sobre cómo se hacen todos esos controles de terceros: usan TemplateBinding. Pero TemplateBinding solo se puede usar en ControlTemplate. http://www.codeproject.com/Tips/599954/WPF-TemplateBinding-with-ControlTemplate
En usercontrol, xaml representa el contenido de UserControl, no ControlTemplate /
Usando esto:
<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" ...
El DataContext de FileBrowserControl ya se ha configurado a sí mismo, por lo tanto, efectivamente se está pidiendo que se enlace al SelectedFile donde DataContext es FileBrowserControl, no el ViewModel padre.
Dale a tu vista un nombre y utiliza un enlace ElementName en su lugar.
SelectedFile="{Binding DataContext.SelectedFile, ElementName=element}"