I have a UserControl that is used in a parent control in this way:
<Views:TranslationTextInput Translation="{Binding SelectedEntity.Name}"/>
The parent control DataContext is a ViewModel containing the SelectedEntity Property.
In my child UserControl I define a new ViewModel as the DataContext:
<UserControl.DataContext>
<vm:TranslationTextInputViewModel x:Name="vm"></vm:TranslationTextInputViewModel>
</UserControl.DataContext>
In the code behind I have:
public static readonly DependencyProperty TranslationProperty = DependencyProperty.Register("Translation", typeof(Translation),typeof(UserControl));
// .NET Property wrapper
public Translation Translation
{
get { return (Translation)GetValue(TranslationProperty); }
set { SetValue(TranslationProperty, value); }
}
public TranslationTextInput(){
InitializeComponent();
DataContext = new TranslationTextInputViewModel();
SetBinding(TranslationProperty, new Binding { Path = new PropertyPath ("Translation"), Mode = BindingMode.OneWayToSource });
When executed I get a Binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'SelectedEntity' property not found on 'object' ''TranslationTextInputViewModel' (HashCode=49954236)'. BindingExpression:Path=SelectedEntity.Name; DataItem='TranslationTextInputViewModel' (HashCode=49954236); target element is 'TranslationTextInput' (Name='InputControl'); target property is 'Translation' (type 'Translation')
It seems the SelectedEntity is looked up on the child UserControl's Viewmodel, but the Property of the parent ViewModel should be used. How can I solve this?
Edit:
public TranslationTextInputViewModel()
{
//EnglishTranslation = tranlsations["en"];
}
public string EnglishTranslation
{
get
{
if (!Translation.TranslationDict.ContainsKey(new CultureInfo("en").LCID))
Translation.Translations.Add(new TranslationItem() { Text = "", Lcid = new CultureInfo("en").LCID });
return Translation.TranslationDict[new CultureInfo("en").LCID].Text;
}
set
{
Translation.TranslationDict[new CultureInfo("en").LCID].Text = value;
}
}
public string SelectedTranslation
{
get
{
if (!Translation.TranslationDict.ContainsKey(_selectedLanguage))
Translation.Translations.Add(new TranslationItem() { Text = "", Lcid = _selectedLanguage });
return Translation.TranslationDict[_selectedLanguage].Text;
}
set
{
Translation.TranslationDict[_selectedLanguage].Text = value;
}
}
private Translation _translation;
public Translation Translation
{
get
{
if (_translation == null)
_translation = new Translation();
return _translation; }
set { _translation = value; }
}
private int _selectedLanguage;
public int SelectedLanguage
{
get
{
return _selectedLanguage;
}
}
public List<CultureInfo> AvailableLanguages
{
get
{
return (from x in PqsLocalization.AvailableLanguages where x.Name != "en" select x).ToList();
}
}
public RelayCommand<int> LanguageChanged { get; private set; }
private void LanguageChangedExecute(int lang)
{
_selectedLanguage = lang;
RaisePropertyChanged("SelectedLanguage");
RaisePropertyChanged("SelectedTranslation");
}
You really shouldn't ever set the DataContext
of a UserControl
from inside the UserControl
. By doing so, you are preventing any other DataContext
from getting passed to the UserControl
, which kind of defeats one of WPF's biggest advantages of having separate UI and data layers.
When your UserControl is being created, you are setting the DataContext
to a new TranslationTextInputViewModel
, and TranslationTextInputViewModel
does not have a property called SelectedEntity
, so your binding fails.
My suggestion? Don't set the DataContext
in the UserControl
.
If you want a specific ViewModel to be used for a specific UserControl, add that to your ParentViewModel
and pass it in as the DataContext
, such as this:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:TranslationTextInputViewModel}">
<Views:TranslationTextInput />
</DataTemplate>
</Window.Resources>
or this:
<Views:TranslationTextInput
DataContext="{Binding SelectedTranslationTextInputViewModel}" />
Or if your ViewModel
contains functionality specific to the UserControl
itself and should not be part of your application layer, put that code in the code-behind the UserControl
and get rid of the ViewModel
altogether.
Try this:
<Views:TranslationTextInput Translation="{Binding SelectedEntity.Name}" DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" />
Once you set the DataContext
your bindings will use it, so I would expect this behaviour -- looking for SelectedEntity.Name
on the TranslationTextInputViewModel
.
There are a few ways to get this working. Personally, I like to model these relationships in the view-models themselves (view-models with view-model properties), but in this situation I'd probably try this, as unpleasant as it feels:
<Views:TranslationTextInput
Translation="{Binding DataContext.SelectedEntity.Name,
RelativeSource={RelativeSource FindAncestor,
AncestorType=ParentControlType}}" />
This is because you set the TranslationTextInput.DataContext to TranslationTextInputViewModel in the constructor.
来源:https://stackoverflow.com/questions/12819044/usercontrol-using-wrong-datacontext