Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Richtiges Binden von selektierten Listen an ein ListItem
MMCSharp
myCSharp.de - Member



Dabei seit:
Beiträge: 22

Themenstarter:

Richtiges Binden von selektierten Listen an ein ListItem

beantworten | zitieren | melden

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.
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Member

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1.782
Herkunft: Düsseldorf

beantworten | zitieren | melden

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
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palladin007 am .
private Nachricht | Beiträge des Benutzers
MMCSharp
myCSharp.de - Member



Dabei seit:
Beiträge: 22

Themenstarter:

beantworten | zitieren | melden

Das habe ich mir eben fast gedacht, deswegen war ich auf der Suche nach einer korrekten Lösung...
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Member

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1.782
Herkunft: Düsseldorf

beantworten | zitieren | melden

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.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palladin007 am .
private Nachricht | Beiträge des Benutzers
MMCSharp
myCSharp.de - Member



Dabei seit:
Beiträge: 22

Themenstarter:

beantworten | zitieren | melden

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>
private Nachricht | Beiträge des Benutzers
Caveman
myCSharp.de - Member

Avatar #avatar-3854.jpg


Dabei seit:
Beiträge: 153

beantworten | zitieren | melden

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);
        }
    }
}
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 9.975

beantworten | zitieren | melden

Warum machst du es so umständlich?
Wenn du SelectedItem bindest, wird doch der Setter aufgerufen und du kannst dort machen was du brauchst.
private Nachricht | Beiträge des Benutzers
MMCSharp
myCSharp.de - Member



Dabei seit:
Beiträge: 22

Themenstarter:

beantworten | zitieren | melden

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?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.832

beantworten | zitieren | melden

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
private Nachricht | Beiträge des Benutzers