1
votes

I have a list view control that uses the DataTemplate to bind the the data. After I created the dependency the binding works with a simple string but will not work when I bind the class property. If I bind the data to a textBlock it works, but if I bind the same thing to my User Control it doesn't work.

Here is my XAML: LISTVIEW

<ListView x:Name='lbUsers'
                 Height='370'
                 Canvas.Left='264'
                 Canvas.Top='183'
                 Width='1177'
                  Background='{x:Null}'>

            <ListView.ItemTemplate>
                <DataTemplate>
                    <WrapPanel>
                        <Views:UserSelectRibbon NameText ="{Binding Name}" />
                        <Image Width='10' />
                    </WrapPanel>
                </DataTemplate>
            </ListView.ItemTemplate>

            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Width="{Binding (FrameworkElement.ActualWidth), 
            RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
                               ItemWidth="{Binding (ListView.View).ItemWidth, 
            RelativeSource={RelativeSource AncestorType=ListView}}"
                               MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
                               ItemHeight="{Binding (ListView.View).ItemHeight, 
            RelativeSource={RelativeSource AncestorType=ListView}}" />
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>

        </ListView>

HERE IS MY USER CONTROL:

  public string NameText
        {
            get { return (string)GetValue(NameTextProperty); }
            set { SetValue(NameTextProperty, value); }
        }

        public static readonly DependencyProperty NameTextProperty =
            DependencyProperty.Register("NameText", typeof(string), typeof(UserSelectRibbon),null);

        public UserSelectRibbon()
        {
            InitializeComponent();
            DataContext = this;
        }

HERE IS MY SIMPLE USER CLASS :

public class User {
    public string Name { get; set; }
    public int Age { get; set; }
    public int Playlist { get; set; }
}

SO: In the XAML if I do this : WORKS

  <Views:UserSelectRibbon NameText ="Some text here" /> 

This will work and add the text to the TextBlock in the user control

BUT: In the XAML if I do this : DOESN'T"T WORK

<Views:UserSelectRibbon NameText ="{Binding Name}" />

I would like to know why it works without the binding but it doesn't work with the binding

2
It is because of DependencyProperty not being visible to the outside of the UserControl, when you look at the declaration namely public static readonly DependencyProperty NameTextProperty = DependencyProperty.Register("NameText", typeof(string), typeof(UserSelectRibbon),null); typeof part defines who has access to the property. The fact that you can set it with normal text is because the backing property is being set.XAMlMAX
@XAMlMAX sorry, that's just wrong. The typeof is defining the Owner, but the property value can still be read/written from the outside.grek40
@grek40 Yes, you can set it through static value i.e. "Some Text" but the Binding will not have access to it and every time you will have a null value there. if you use it inside of the UserControl Declaration it will work, outside usage will not. Go ahead test it.XAMlMAX
@XAMlMAX Ok, I went ahead and tried it, thanks for wasting my time, it works of course as expected.grek40
Ah I see I was wrong, I am sorry. @grek40 thanks for keeping it civil ;-)XAMlMAX

2 Answers

4
votes

The problem is with your DataContext:

public UserSelectRibbon()
{
    InitializeComponent();
    DataContext = this;
}

This has an implication on the following:

<Views:UserSelectRibbon NameText ="{Binding Name}" />

In here, the DataContext is already changed to the Views:UserSelectRibbon, so you can't bind to anything from the outer DataContext anymore.

Solution: do not set the DataContext of a UserControl to itself from the inside. Never!

Instead, set it on an element inside the usercontrols tree or use some RelativeSource or ElementName binding.

Solution with inner DataContext:

<UserControl ...>
    <Grid x:Name="grid1">
        <TextBlock Text="{Binding NameText}"/>
    </Grid>
</UserControl>

and

public UserSelectRibbon()
{
    InitializeComponent();
    grid1.DataContext = this; // set DataContext on inner control instead of the usercontrol itself
}

Solution with RelativeSource (you can use UserControl base type or your specific usercontrols inner type):

<TextBlock Text="{Binding NameText,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>

Solution with ElementName:

<UserControl ...
             x:Name="myControl">
    <Grid>
        <TextBlock Text="{Binding NameText,ElementName=myControl}"/>
    </Grid>
</UserControl>
-1
votes

I did something like this recently with an itemsControl. I'm probably gonna forget everything though. Anyway, there's a simpler way then binding straight from the item itself. Instead, you call the item from within your c# and have the binding there. After

Mainpage()
{
InitializeComponent();
}

Still in the brackets, you should add this:

List<SampleItem> = new List<SampleItem>;
items.Add({NameText = Name});
lbusers.ItemsSource = items;

And outside the brackets:

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

You can add the items.Add line as many times as you want, and that's how many items will show up.