0
votes

I've tried solving this myself, looking at several possible solutions here on Stack Overflow, but alas I've been unable to solve this issue.


TL;DR version:

The problem:

A listBox using databinding to show a list of RPG characters, which have a nested property for their attributes. I can't get the attributes to show due to the limitations with nested properties and databindings. The code below is related to the issue.


I have a listBox that has a databinding that controls what is shown in the list. The databinding uses ObservableCollection for the list of objects that the list contains. All this works fine, but is related to the issue at hand.

The listBox databinding has several nested properties in each element, that I want to display and change in the form, yet I cannot get nested databinding to work correctly.

This is the listBox XAML:

<ListBox x:Name="listCharacters" Margin="2,0" ItemsSource="{Binding}" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Hidden" SelectionChanged="listCharacters_SelectionChanged">
    <ListBox.ItemTemplate>
        <DataTemplate DataType="{x:Type local:Character}" x:Name="Symchar">
            <Grid Width="125" HorizontalAlignment="Left" Background="{x:Null}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="18"/>
                    <RowDefinition Height="12"/>
                    <RowDefinition Height="16"/>
                </Grid.RowDefinitions>
                <Image Panel.ZIndex="5" HorizontalAlignment="Right" VerticalAlignment="Top" Height="16" Width="16" Margin="0,2,0,0" Source="Resources/1454889983_cross.png" MouseUp="DeleteCharacter" />
                <TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" FontWeight="Bold" FontSize="13.333" Grid.Row="0" TextTrimming="CharacterEllipsis" Padding="0,0,16,0" />
                <TextBlock Text="{Binding RealRace.Label, UpdateSourceTrigger=PropertyChanged}" FontSize="9.333" Grid.Row="1" FontStyle="Italic" />
                <TextBlock FontSize="9.333" Grid.Row="2" HorizontalAlignment="Left" VerticalAlignment="Top">
                    <Run Text="{Binding RealClass.Archetype.Label, UpdateSourceTrigger=PropertyChanged}"/>
                    <Run Text=" - "/>
                    <Run Text="{Binding RealClass.Label, UpdateSourceTrigger=PropertyChanged}"/>
                </TextBlock>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

And setting the listBox ItemSource:

this.listCharacters.ItemsSource = CharacterList;

This is the character class, I removed unrelated code (XML serialization attributes etc.)

public class Character : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            this.NotifyPropertyChanged("Name");
        }
    }

    private string _player;
    public string Player
    {
        get { return _player; }
        set
        {
            _player = value;
            this.NotifyPropertyChanged("Player");
        }
    }

    private string _race;
    public string Race
    {
        get { return _race; }
        set
        {
            _race = value;
            this.NotifyPropertyChanged("Race");
        }
    }

    private Race _realRace;
    public Race RealRace
    {
        get { return _realRace; }
        set
        {
            _realRace = value;
            Race = value.Id;
            this.NotifyPropertyChanged("RealRace");
        }
    }

    private string _gender;
    public string Gender
    {
        get { return _gender; }
        set
        {
            _gender = value;
            this.NotifyPropertyChanged("Gender");
        }
    }

    private Attributes _attributes;
    public Attributes Attributes
    {
        get { return _attributes; }
        set
        {
            _attributes = value;
            this.NotifyPropertyChanged("Attributes");
        }
    }

    private string _class;
    public string Class
    {
        get { return _class; }
        set
        {
            _class = value;
            this.NotifyPropertyChanged("Class");
        }
    }

    private Class _realClass;
    public Class RealClass
    {
        get { return _realClass; }
        set
        {
            _realClass = value;
            Class = value.Id;
            this.NotifyPropertyChanged("RealClass");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

To keep it simple, the property that I've been testing with, is the 'Attributes' property, this is the code for it:

public class Attributes : INotifyPropertyChanged
{
    private int _accurate;
    public int Accurate
    {
        get { return _accurate; }
        set
        {
            _accurate = value;
            this.NotifyPropertyChanged("Accurate");
        }
    }

    private int _cunning;
    public int Cunning
    {
        get { return _cunning; }
        set
        {
            _cunning = value;
            this.NotifyPropertyChanged("Cunning");
        }
    }

    private int _discreet;
    public int Discreet
    {
        get { return _discreet; }
        set
        {
            _discreet = value;
            this.NotifyPropertyChanged("Discreet");
        }
    }

    private int _persuasive;
    public int Persuasive
    {
        get { return _persuasive; }
        set
        {
            _persuasive = value;
            this.NotifyPropertyChanged("Persuasive");
        }
    }

    private int _quick;
    public int Quick
    {
        get { return _quick; }
        set
        {
            _quick = value;
            this.NotifyPropertyChanged("Quick");
        }
    }

    private int _resolute;
    public int Resolute
    {
        get { return _resolute; }
        set
        {
            _resolute = value;
            this.NotifyPropertyChanged("Resolute");
        }
    }

    private int _strong;
    public int Strong
    {
        get { return _strong; }
        set
        {
            _strong = value;
            this.NotifyPropertyChanged("Strong");
        }
    }

    private int _vigilant;
    public int Vigilant
    {
        get { return _vigilant; }
        set
        {
            _vigilant = value;
            this.NotifyPropertyChanged("Vigilant");
        }
    }

    private int _toughness;
    public int Toughness
    {
        get { return _toughness; }
        set
        {
            _toughness = value;
            this.NotifyPropertyChanged("Toughness");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

I want to display each individual attribute in a field when a character in the listBox is selected, this works fine with properties directly in the character class, but due to the limitations on nested properties and databindings, I haven't been able to get it to work with the 'Attributes' properties values.

XAML for one of the attribute input fields:

<TextBox x:Name="attr_Accurate" DataContext="{Binding Path=(local:Character.Attributes), XPath=SelectedItem, ElementName=listCharacters, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Path=(local:Accurate)}" PreviewTextInput="NumericInput"/>

The UpdateSourceTrigger is simply a method to only allow integers to be input in the field:

private void NumericInput(object sender, TextCompositionEventArgs e)
{
    if (!char.IsDigit(e.Text, e.Text.Length - 1))
    {
        e.Handled = true;
    }
}

If anyone could help me get the values within the selected character's attributes to show up via databindings, I would greatly appreciate it.

1
Can you provide full sample?Ayyappan Subramanian
@AyyappanSubramanian I would have to ask the client, do you want the full solution?Dion S. Jensen

1 Answers

1
votes

Use binding as following:

To Show Accurate Property value:

<TextBox x:Name="attr_Accurate" Text="{Binding Path=SelectedItem.Attributes.Accurate), ElementName=listCharacters, Mode=OneWay}"  PreviewTextInput="NumericInput"/>

To Show Cunning property value:

<TextBox x:Name="attr_Accurate" Text="{Binding Path=SelectedItem.Attributes.Cunning), ElementName=listCharacters, Mode=OneWay}"  PreviewTextInput="NumericInput"/>

and so one.

(I'm not sure if you want binding to be two way or one so please change as your need)