2
votes

I have the following scenario:

public class HubModel
{
    public string Name { get; set; }
}

I create an ObservableCollection in my ViewModel and set the DataContext on the HubPage to that ViewModel.

On my HubPage I have a simple UserControl called TestUserControl.

XAML from the UserControl:

<UserControl
    x:Name="userControl"
    ....>
    <Grid>
        <StackPanel Orientation="Vertical">
            <ItemsControl x:Name="ItemsContainer" ItemsSource="{Binding Collection}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Margin="0,0,0,20">
                            <StackPanel>
                                <TextBlock Foreground="Black" HorizontalAlignment="Left" FontFamily="Arial" FontSize="42" VerticalAlignment="Center" Name="CurrencyTextBlock" Text="{Binding Path=Text,ElementName=userControl}"></TextBlock>
                            </StackPanel>
                        </Button>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Grid>
</UserControl>

UserControl code behind:

public ObservableCollection<object> Collection
{
    get { return (ObservableCollection<object>)GetValue(CollectionProperty); }
    set { SetValue(CollectionProperty, value); }
}

public static readonly DependencyProperty CollectionProperty =
    DependencyProperty.Register("Collection", typeof(ObservableCollection<object>), typeof(TestUserControl), new PropertyMetadata(null));


public string Text
{
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value); }
}

public static readonly DependencyProperty TextProperty =
    DependencyProperty.Register("Text", typeof(string), typeof(TestUserControl), new PropertyMetadata(string.Empty));

Because my UserControl should not know HubModel I wanna bind the TextBlock Text-Path via DependencyProperty.

XAML from the HubPage:

...
<userControls:TestUserControl Collection="{Binding TestCollection}" Text="Name"/>
...

Collection="{Binding TestCollection}" sets the list to the DependencyProperty in my UserControl.

Text="Name" sets the property name. The plan is that my UserControl finds for the TextBlock Text "Name" in the DependencyProperty and takes the value from the Property "Name" from the binded class HubModel.

The problem is that my UserControl finds "Name" in the DependencyProperty and shows "Name" for every entry in the collection instead of the property value in the class.

Is something like this possible? Or what's the best way for binding within UserControls. In my optionen the should not know the property name from the binded classes.

Thanks Daniel

1

1 Answers

1
votes

The tricky part here is that you're essentially trying to bind a property of a Binding itself (namely Binding.Path). That is not possible, because Binding is not a DependencyObject, and Binding.Path is not a dependency property. So you have to step back and find another approach.

One would be to create a subclass of TextBlock, with added dependency properties SourceObject (for the object, "HubModel" in this case), and PropertyName (for the property to display, "Name"), or similar. That would allow you to update the Text, by using reflection.

So you would write this instead of TextBlock:

<my:ExtendedTextBlock SourceObject="{Binding}" PropertyName="{Binding Path=Text,ElementName=userControl}" />

And ExtendedTextBlock would look something like this:

public class ExtendedTextBlock : TextBlock
{
    public object SourceObject
    {
        get { return GetValue(SourceObjectProperty); }
        set { SetValue(SourceObjectProperty, value); }
    }
    public static readonly DependencyProperty SourceObjectProperty = 
        DependencyProperty.Register("SourceObject", 
            typeof(object), 
            typeof(ExtendedTextBlock), 
            new PropertyMetadata(UpdateText)
        );

    public string PropertyName
    {
        get { return GetValue(PropertyNameProperty); }
        set { SetValue(PropertyNameProperty, value); }
    }
    public static readonly DependencyProperty PropertyNameProperty = 
        DependencyProperty.Register("PropertyName", 
            typeof(string), 
            typeof(ExtendedTextBlock), 
            new PropertyMetadata(UpdateText)
        );

    public static void UpdateText(object sender, DependencyPropertyChangedEventArgs args) 
    {
        var owner = (ExtendedTextBlock)sender;
        if (owner.SourceObject == null || string.IsNullOrEmpty(owner.PropertyName))
            return;

        var prop = SourceObject.GetType().GetProperty(PropertyName); 
        if (prop == null)
            return;

        var val = prop.GetValue(SourceObject, null);
        owner.Text = (val == null ? "" : val.ToString());
    }
}

(In WPF you could use MultiBinding for this, but that does not exist in WinRT so far as I know.)