Laden...

Richtiges Binden von selektierten Listen an ein ListItem

Erstellt von MMCSharp vor einem Jahr Letzter Beitrag vor einem Jahr 697 Views
M
MMCSharp Themenstarter:in
84 Beiträge seit 2022
vor einem Jahr
Richtiges Binden von selektierten Listen an ein ListItem

Hallo

Ich würde gerne eine Bindung an eine Liste von Werten an eine Liste erstellen. Ich habe jetzt schon einiges versucht, jedoch hatte ich entweder kein Ergebnis ein fehlerhaftes oder eine fragwürdige Vorgehensweise.

Mein zu erreichendes Ziel ist:

-Eine Abfrage mit Linq an eine SQL-Datenbank
-Das Ergebnis (eine Liste von Daten) an eine LsitBox Binden
-Die Ausgabe der Daten über einen Item Container ( Personalisiertes Item mit mehreren Controls)

Bereits gelungen ist mir diese Aufgabe mit einem Code Behind. Jedoch ist hier die Vorgehensweise Fragwürdig, da die Daten linear geladen werden. Zudem würde ich gerne "korrekt" vorgehen, wie es richtig mit WPF umgesetzt wird.


        private void AddLocationsToList()
        {
           
            var LocationsList = (from tbl_addresses in dataContext.tbl_addresses
                                 select new
                                 {
                                     tbl_addresses.Id,
                                     tbl_addresses.StockName,
                                     tbl_addresses.City,
                                     tbl_addresses.Postcode,
                                     tbl_addresses.Street,
                                     tbl_addresses.HouseNumber
                                 });

            foreach (var Item in LocationsList)
            {
                
                Grid grid1 = new Grid();

                RowDefinition r1 = new RowDefinition
                {
                    Height = new GridLength(30, GridUnitType.Star)
                };

                RowDefinition r2 = new RowDefinition
                {
                    Height = new GridLength(30, GridUnitType.Star)
                };
                
                RowDefinition r3 = new RowDefinition
                {
                    Height = new GridLength(30, GridUnitType.Star)
                };

                grid1.RowDefinitions.Add(r1);
                grid1.RowDefinitions.Add(r2);
                grid1.RowDefinitions.Add(r3);


                //Label for Warehouse ID
                Label IDLabel = new Label
                {
                    Margin = new Thickness(2, 0, 0, 0),
                    Content = Item.Id.ToString()+ " - " + Item.StockName.ToString(),
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    Foreground = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255)),
                    FontWeight = FontWeights.Bold
                };

                Grid.SetRow(IDLabel, 1);

                //Label for Warehouse Name
                Label StockNameLabel = new Label
                {
                    Margin = new Thickness(2, 1, 0, 0),
                    Content = Item.Postcode.ToString() + " " + Item.City.ToString() + "\n" + Item.Street.ToString() + " " + Item.HouseNumber.ToString(),
                    //Background= new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)),    
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    Foreground = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255))
                };

                Grid.SetRow(StockNameLabel, 2);

                
                grid1.Children.Add(IDLabel);
                grid1.Children.Add(StockNameLabel);

                Grid.SetColumn(grid1,1);

                

                Grid grid2 = new Grid();

                ColumnDefinition c1 = new ColumnDefinition
                {
                   Width = new GridLength(35, GridUnitType.Star)
                };

                ColumnDefinition c2 = new ColumnDefinition
                {
                    Width = new GridLength(35, GridUnitType.Star)
                };

                grid2.ColumnDefinitions.Add(c1);
                grid2.ColumnDefinitions.Add(c2);

                SharpVectors.Converters.SvgViewbox svgViewbox = new SharpVectors.Converters.SvgViewbox()
                {

                    Source = new Uri("pack://application:,,,/Pages/ParkingLot/Icons/warenhaus.svg"),
                    Height = 30,
                    Width = 30,
                    HorizontalAlignment = HorizontalAlignment.Left

                };

                Grid.SetColumn(svgViewbox, 0);

                
                grid2.Children.Add(svgViewbox);
                grid2.Children.Add(grid1);

                ListLocations.Items.Add(grid2);
            }


        }

Über verweise auf gute Tutorials oder einige nachvollziehbare Beispiele würde ich mich sehr freuen.

2.079 Beiträge seit 2012
vor einem Jahr

Joa - Du bastelst dir alles selber, ohne DataBinding wirst Du keine Bindung hin bekommen.

Oder anders gesagt: Deinen Code kannst Du komplett entsorgen - so mies das auch klingen mag.
Bis auf das LINQ am Anfang (wobei das da auch nicht hin gehört, aber das ist ein anderes Thema) ist der komplette Rest ein MVVM-Verstoß und kann/sollte vollständig mit DataBinding, einem ItemsControl (oder ListBox) und ItemTemplate im XAML gelöst werden.

[Artikel] MVVM und DataBinding

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

M
MMCSharp Themenstarter:in
84 Beiträge seit 2022
vor einem Jahr

Das habe ich mir eben fast gedacht, deswegen war ich auf der Suche nach einer korrekten Lösung...

2.079 Beiträge seit 2012
vor einem Jahr

The ItemsControl - The complete WPF tutorial

Wobei das auch kein richtiges MVVM ist, weil das ViewModel fehlt.
Wie das richtig geht, steht in meinem letzten Link.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

M
MMCSharp Themenstarter:in
84 Beiträge seit 2022
vor einem Jahr

Nun versuche ich mich am MVVM, allerdings ist das echt nicht so einfach. Es ist enormes Umdenken notwendig und bringt mich echt nahe zur Verzweiflung. Ich habe das Item nun über ein ItemTemplate erstellt und "bediene" das UI jetzt via ViewModel. Nun würde ich gern über einen Select in der ListBox, eine Property auslesen und in einen andere übertragen um damit weiter zu arbeiten. Mein Ansatz dazu wäre mit einem DataTrigger das selektierte Item auszulesen und danach mit dem Setter, die Property des enthaltenen TextBlock zu übertragen. Jedoch hänge ich fest. Ich finde keinen Weg im Setter die Property im VM anzusprechen und zu füllen.

( -TargetName="LocationId"- funktioniert eben leider nicht, da es eben kein Member des Item ist, sondern eine Property im VM)


                                    <DataTemplate.Triggers>
                                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource 
                                                                        Mode=FindAncestor, AncestorType={x:Type ListBoxItem}},
                                                                        Path=IsSelected}" Value="True">
                                            <Setter TargetName="LocationId" Value="{Binding RelativeSource={RelativeSource 
                                                                        Mode=FindAncestor,AncestorType= {x:Type TextBlock}},
                                                                        ElementName=LblID}"/>
                                        </DataTrigger>
                                    </DataTemplate.Triggers>

187 Beiträge seit 2009
vor einem Jahr

Ich mache das mit dem Nuget-Paket Microsoft.Xaml.Behaviors.Wpf.


<Window x:Class="CSharp124655.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CSharp124655"
        xmlns:behav="http://schemas.microsoft.com/xaml/behaviors"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>
        <DataTemplate x:Key="AddressTemplate" DataType="{x:Type local:AddressViewModel}">
            <Border BorderThickness="2"
                    BorderBrush="Black"
                    CornerRadius="3"
                    Padding="3">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="10" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Row="0"
                           Grid.Column="0">
                    <Run Text="ID: " />
                    <Run Text="{Binding Id}" />
                </TextBlock>
                <TextBlock Grid.Row="1"
                           Grid.Column="0">
                    <Run Text="StockName: " />
                    <Run Text="{Binding StockName}" />
                </TextBlock>
                <TextBlock Grid.Row="2"
                           Grid.Column="0">
                    <Run Text="City: " />
                    <Run Text="{Binding City}" />
                </TextBlock>
                <TextBlock Grid.Row="0"
                           Grid.Column="2">
                    <Run Text="Postal code: " />
                    <Run Text="{Binding Postcode}" />
                </TextBlock>
                <TextBlock Grid.Row="1"
                           Grid.Column="2">
                    <Run Text="Street: " />
                    <Run Text="{Binding Street}" />
                </TextBlock>
                <TextBlock Grid.Row="2"
                           Grid.Column="2">
                    <Run Text="HouseNumber: " />
                    <Run Text="{Binding HouseNumber}" />
                </TextBlock>
            </Grid>
            </Border>
        </DataTemplate>
    </Window.Resources>

    <Window.DataContext>
        <local:MainWindowViewModel x:Name="DataContext" />
    </Window.DataContext>
    
    <Grid>
        <ListBox x:Name="AddressesListBox"
                 ItemsSource="{Binding Addresses}"
                 SelectedItem="{Binding SelectedAddress}"
                 SelectionMode="Single" 
                 ItemTemplate="{StaticResource AddressTemplate}">
            <behav:Interaction.Triggers>
                <behav:EventTrigger EventName="SelectionChanged">
                    <behav:InvokeCommandAction Command="{Binding OnSelectionChanged}" />
                </behav:EventTrigger>
            </behav:Interaction.Triggers>
        </ListBox>

    </Grid>
</Window>


using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Input;

namespace CSharp124655
{
    public class MainWindowViewModel : ViewModelBase
    {
        public ObservableCollection<AddressViewModel> Addresses { get; private set; }

        public AddressViewModel SelectedAddress { get; set; }

        public ICommand OnSelectionChanged { get; private set; }

        public MainWindowViewModel()
        {
            Addresses = new ObservableCollection<AddressViewModel>();
            for (int i = 1; i < 6; i++)
            {
                Addresses.Add(new AddressViewModel() { City = $"City{i}", HouseNumber = i, Id = i + 10000, Postcode = $"12340{i}", StockName = $"Name{i}", Street = $"Street{i}" });
            }

            OnSelectionChanged = new RelayCommand(o => { ContinueWithSelectedItem(); });
        }

        private void ContinueWithSelectedItem()
        {
            Debug.WriteLine(SelectedAddress.StockName);
        }
    }
}

F
10.010 Beiträge seit 2004
vor einem Jahr

Warum machst du es so umständlich?
Wenn du SelectedItem bindest, wird doch der Setter aufgerufen und du kannst dort machen was du brauchst.

M
MMCSharp Themenstarter:in
84 Beiträge seit 2022
vor einem Jahr

Super ! Vielen Dank @Caveman. Die Lösung mit dem Nuget-Paket gefällt mir sehr gut, da es das Event-Handling allgemein erleichtert.

Jetzt habe ich noch eine Frage:

Ich würde gern die Listbox Async laden, um ein einfrieren des UI zu verhindern. Wie lässt sich das noch umsetzen in MVVM?

16.835 Beiträge seit 2008
vor einem Jahr

Punkt 1): Man erzeugt/lädt keine Daten im Konstruktor, auch das blockt/kann blocken.
Punkt 2): immer mit Commands arbeiten; erster Google Treffer: MVVM - Going async with async command