0

So I'm creating "cards" to visually represent a collection of objects within a StackPanel (which I'm using a list to hold these objects):

MainWindow XAML:

<Window /* ... */>
    <StackPanel x:Name="Deck" Orientation="Horizontal" />
</Window>

MainWindow C#:

public partial class MainWindow : Window
{
    /* ... */
    private void OnDataReceived(List<Reading> readings)
    {
        foreach(Reading r in readings)
        {
            Deck.Children.Add(new Card
            {
                Id = r.Id,
                Value = r.Value
            });
        }
    }
}

UserControl XAML:

<UserControl /* ... */ x:Name="crd">
    <Label Content="{Binding Path=Id, ElementName=crd}" />
    <Label Content="{Binding Path=Value, ElementName=crd} />
</UserControl>

UserControl C#:

public partial class LoggerRepresentation : UserControl
{
    public string Id { get; set; }
    public int Value { get; set; }
    /* ... */
}

Upon adding one element to Deck.Children, its' visual representation does appear in the StackPanel as expected. However, DP seems to lack something as the Labels binding Id and Value remain empty.

(The idea to give x:Name="crd" to my UserControl and then use it within the Binding as ElementName has been plugged from the answer to a seemingly related question, but may be misleading in this case.)

draconigen
  • 93
  • 1
  • 12
  • 3
    It looks like you're doing things like its used to be in windows programming. you should check MVVM and INotifyPropertyChanged interface. – Canavar Sep 07 '18 at 11:13

1 Answers1

0

You should use an ItemsControl whenever you want to display a dynamic collection of items.

Replace the StackPanel in your XAML markup with an ItemsControl:

<ItemsControl ItemsSource="{Binding Cards}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <local:LoggerRepresentation />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Bind directly to the source properties of the corresponding Card object in the UserControl (and replace the Label elements with TextBlocks):

<TextBlock Text="{Binding Path=Id}" />
<TextBlock Text="{Binding Path=Value} />

Create a view model class that exposes a collection of Card objects through a public property and move your application logic over to this one:

public class ViewModel
{
    public ObservableCollection<Card> Cards { get; } = new ObservableCollection<Card>();

    //...
    private void OnDataReceived(List<Reading> readings)
    {
        foreach (Reading r in readings)
        {
            Cards.Add(new Card
            {
                Id = r.Id,
                Value = r.Value
            });
        }
    }
}

Set the DataContext of the view where the ItemsControl is defined to an instance of the view model class:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new ViewModel();
    }
}

This design pattern is known as Model-View-ViewModel (MVVM) and it's the recommended pattern to use when developing XAML based UI applications. You will find a lot more to read and learn about it if your Google.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • `` isn't allowed inside ``, but is accepted when I wrap the Template tag in ``. Is that correct? – draconigen Sep 07 '18 at 13:01
  • I still seem to get it wrong, at least I'm getting errors to the output now: `System.Windows.Data Error: 40 : BindingExpression path error: 'Value' property not found on 'object' ''ViewModel' (HashCode=20770076)'. BindingExpression:Path=Value; DataItem='ViewModel' (HashCode=20770076); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')` Is the "Cards" in `` referencing the "Cards" in public `ObservableCollection Cards { get; } = new ObservableCollection();`? That's beyond me still – draconigen Sep 07 '18 at 13:08
  • It should, unless you override the DataContext somewhere. – mm8 Sep 07 '18 at 13:10
  • How would I ideally add items to the list from the MainWindow now? I've added a function "Add()" to the ViewModel class and am simply routing received items into the Cards property. Is that correct? – draconigen Sep 07 '18 at 13:26
  • You should use commands. But that's another story. Please ask a new question if you have another issue. – mm8 Sep 07 '18 at 13:33
  • The issue is still at hand and errors are still present; DataContext hasn't been overridden anywhere else. Empty cards started appearing only after I turned ViewModel into a singleton class, but I suppose that's straying even further from the solution. I'll let it rest and look at the whole code with rested eyes again, thank you so far. :) – draconigen Sep 07 '18 at 13:38
  • Singleton class? What difference would that make? Are you creating several instance of your view model? Put a breakpoint in the constructor to find out. I assume that Id and Value are *public properties* of the Card class. – mm8 Sep 07 '18 at 14:16