Laden...

ListBox items aktualisieren + Focus

Erstellt von Taucher vor 3 Jahren Letzter Beitrag vor 3 Jahren 369 Views
T
Taucher Themenstarter:in
307 Beiträge seit 2008
vor 3 Jahren
ListBox items aktualisieren + Focus

Hallo Leute,

ich versuche mich gerade mal wieder an Wpf, und komme leider nicht weiter. Es geht darum, das die Items in meiner ListBox nicht aktualisiert werden. Ich habe da hier im Forum und über die Suchmaschine bereits Beispiele gefunden, aber leider komme ich damit nicht weiter.

Kurz zum Aufbau des Programms:
Ich habe ein MainWindow, das in 2 Hälften (links und rechts) aufgeteilt ist. Auf der linken Seite befinden sich Buttons, wo bei entsprechendem Klick, rechts ein UserControl geladen werden soll. Das funktioniert soweit bereits prima.
Das neu geladene UserControl ist wiederrum in 2 Hälften (oben und unten) aufgeteilt, wobei sich oben wieder Buttons befinden, und bei entsprechendem Klick, unten ein weiteres UserControl geladen wird. Auch das funktioniert soweit schon mal alles prima.
Allerdings fangen hier dann die Probleme schon einmal an.
Ich habe in dem UserControl das dann im unteren Bereich geladen wird, eine TextBox. Diese soll beim laden fokusiert werden, das versuche ich mit .Focus(). Der Cursor befindet sich zwar in der TextBox, allerdings blinkt er nicht, und wenn ich per Tastatur dann versuche eine Eingabe zu machen, ist die TextBox nicht fokusiert, also es tut sich nichts.

Das andere ist, ich habe in diesem UserControl auch eine ListBox, wo ich Einträge hinzufügen und löschen kann. Datenbanktechnisch werden die Einträge auch gelöscht bzw. angelegt, aber die ListBox bzw. deren Items werden dabei nicht aktualisiert.
Wie oben bereits erwähnt, komme ich jetzt hier nicht weiter und bräuchte da Hilfe dazu.

Hier mal die ganzen Klassen und xml´s dazu: (Ich weiß das es viele sind...)
Das Model mit Interface (DisplayMemberModel, IDisplayMemberModel):


public interface IDisplayMemberModel : INotifyPropertyChanged
    {
        string DisplayMember { get; set; }
        int Id { get; set; }
    }
public class DisplayMemberModel : IDisplayMemberModel
    {
        #region ... vars
        private string _displayMember;
        private int _id;
        #endregion

        #region ... properties
        public string DisplayMember
        {
            get => this._displayMember ?? string.Empty;
            set
            {
                if (this._displayMember != value)
                {
                    this._displayMember = value;
                    this.NotifyPropertyChanged("DisplayMember");
                }
            }
        }
        public int Id
        {
            get => this._id;
            set
            {
                if (this._id != value)
                {
                    this._id = value;
                    this.NotifyPropertyChanged("Id");
                }
            }
        }
        #endregion

        #region ... events
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
        #endregion
    }

Hier die ViewModels mit den dazugehörigen Views:


public abstract class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public virtual void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
public class MainWindowViewModel : BaseViewModel
    {
        #region ... vars
        private ObservableCollection<IDisplayMemberModel> _naviButtons = new ObservableCollection<IDisplayMemberModel>();
        #endregion

        #region ... constructor
        public MainWindowViewModel()
        {
            this.createNaviButtons();
        }
        #endregion

        #region ... properties
        public ObservableCollection<IDisplayMemberModel> NaviButtons
        {
            get => this._naviButtons;
            set
            {
                if(this._naviButtons != value)
                {
                    this._naviButtons = value;
                    this.OnPropertyChanged("NaviButtons");
                }
            }
        }
        #endregion

        #region ... methods
        private void createNaviButtons()
        {
            this.NaviButtons.Add(new DisplayMemberModel() { DisplayMember = TextProvider.GetFormattedText(EnumIdentifier.overview), Id = (int)EnumIdentifier.overview });
            this.NaviButtons.Add(new DisplayMemberModel() { DisplayMember = TextProvider.GetFormattedText(EnumIdentifier.categories), Id = (int)EnumIdentifier.categories });
            this.NaviButtons.Add(new DisplayMemberModel() { DisplayMember = TextProvider.GetFormattedText(EnumIdentifier.product_settings), Id = (int)EnumIdentifier.product_settings });
        }
        #endregion
    }
public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //  Deklariert das ViewModel für das MainWindow
            DataContext = new MainWindowViewModel();
        }
        
        private void MenuItem_CloseClick(object sender, RoutedEventArgs e)
        {
            this.closeApplication(sender, e);
        }
        private void closeApplication(object sender, RoutedEventArgs e)
        {
            //  Routine zum Schließen der Anwendung hier einfügen
            Application.Current.Shutdown();
        }

        void naviButton_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            if (btn == null) return;
            int id;
            if (!int.TryParse(btn.Tag.ToString(), out id)) return;
            this.gvContent.Children.Clear();
            switch(id)
            {
                case 0:
                    break;
                case (int)EnumIdentifier.categories:
                    this.gvContent.Children.Add(new CtrlProductCategories());
                    break;
                case (int)EnumIdentifier.product_settings:
                    this.gvContent.Children.Add(new CtrlProductSettings());
                    break;
            }
        }
    }


<Window x:Class="ProductManagement.Wpf.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="600" Width="800"
        WindowStartupLocation="CenterScreen" WindowState="Maximized">
    <Window.Resources>
        <Style x:Key="CustomGridSplitterStyle" TargetType="GridSplitter">
            <Setter Property="Background">
                <Setter.Value>
                    <SolidColorBrush>
                        <SolidColorBrush.Color>
                            <Color A="255" R="155" G="155" B="155" />
                        </SolidColorBrush.Color>
                    </SolidColorBrush>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300px" MinWidth="300" MaxWidth="600" />
            <ColumnDefinition Width="5" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <DockPanel Grid.ColumnSpan="3" Name="dpTrv">
            <Menu DockPanel.Dock="Top">
                <MenuItem Header="_Datei">
                    <Separator />
                    <MenuItem Header="_Schließen" Click="MenuItem_CloseClick"/>
                </MenuItem>
                <MenuItem Header="_Bearbeiten">
                    <MenuItem Header="_Produkte">
                        <MenuItem Header="_Übersicht" />
                        <MenuItem Header="_Produkt erstellen" />
                    </MenuItem>
                </MenuItem>
                <MenuItem Header="_Datenbestand">
                    <MenuItem Header="_Daten synchronisieren">
                        <MenuItem Header="_Alle" />
                        <MenuItem Header="Quellen auswählen" />
                    </MenuItem>
                </MenuItem>
                <MenuItem Header="Test">
                    <MenuItem Header="Overview" />
                </MenuItem>
            </Menu>
        </DockPanel>
        <GridSplitter Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Width="3" HorizontalAlignment="Stretch" Style="{StaticResource CustomGridSplitterStyle}">
            
        </GridSplitter>
        <DockPanel Grid.Column="2" Grid.Row="1" Name="gvContent">
            
        </DockPanel>
        <DockPanel Grid.Column="0" Grid.Row="1">
            <Grid VerticalAlignment="Top">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <StackPanel Name="naviButtonContainer">
                    <ItemsControl ItemsSource="{Binding NaviButtons}">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Button Content="{Binding DisplayMember}" Tag="{Binding Id}" Click="naviButton_Click" />
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </StackPanel>
            </Grid>
        </DockPanel>
    </Grid>
</Window>



public class CtrlProductSettingsViewModel : BaseViewModel
    {
        #region ... vars
        private ObservableCollection<IDisplayMemberModel> _naviButtons = new ObservableCollection<IDisplayMemberModel>();
        #endregion

        #region ... constructor
        public CtrlProductSettingsViewModel()
        {
            this.createNaviButtons();
        }
        #endregion

        #region ... properties
        public ObservableCollection<IDisplayMemberModel> NaviButtons
        {
            get => this._naviButtons;
            set
            {
                if(this._naviButtons != value)
                {
                    this._naviButtons = value;
                    this.OnPropertyChanged("NaviButtons");
                }
            }
        }
        #endregion

        #region ... methods
        private void createNaviButtons()
        {
            this.NaviButtons.Add(new DisplayMemberModel() { DisplayMember = TextProvider.GetFormattedText(EnumIdentifier.brands), Id = (int)EnumIdentifier.brands });
        }
        #endregion
    }
public partial class CtrlProductSettings : UserControl
    {
        #region ... constructor
        public CtrlProductSettings()
        {
            InitializeComponent();
            this.DataContext = new CtrlProductSettingsViewModel();
        }
        #endregion

        #region ... methods
        
        #endregion

        #region ... events
        private void naviButton_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            if (btn == null) return;
            int id;
            if (!int.TryParse(btn.Tag.ToString(), out id)) return;
            this.UserControlContainer.Children.Clear();
            switch(id)
            {
                case (int)EnumIdentifier.brands:
                    this.UserControlContainer.Children.Add(new CtrlBrands());
                    break;
            }
        }
        #endregion
    }


<UserControl x:Class="ProductManagement.Wpf.Views.CtrlProductSettings"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ProductManagement.Wpf.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <Style x:Key="button" TargetType="Button">
            <Setter Property="Height" Value="125"/>
            <Setter Property="Width" Value="125"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
        </Style>
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <DockPanel Grid.Row="0" Name="buttonContainer">
            <ItemsControl ItemsSource="{Binding NaviButtons}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Content="{Binding DisplayMember}" Tag="{Binding Id}" Click="naviButton_Click" Style="{StaticResource button}"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </DockPanel>

        <DockPanel Grid.Row="1" Name="UserControlContainer">
            
        </DockPanel>
    </Grid>
</UserControl>



public class CtrlBrandsViewModel : BaseViewModel
    {
        #region ... vars
        private ObservableCollection<IDisplayMemberModel> _brands = new ObservableCollection<IDisplayMemberModel>();
        #endregion

        #region ... constructor
        public CtrlBrandsViewModel()
        {
            this.loadBrandItems(null);
        }
        #endregion

        #region ... properties
        public ObservableCollection<IDisplayMemberModel> Brands
        {
            get => this._brands;
            set
            {
                if(this._brands != value)
                {
                    this._brands = value;
                    this.OnPropertyChanged("Brands");
                }
            }
        }
        #endregion

        #region ... methods
        private void loadBrandItems(string searchString)
        {
            IList<IBrandData> brands = ProductManagement.Storage.Manager.MainProductPropertyManager.Current.GetBrands(searchString);

            foreach(IBrandData brand in brands)
            {
                this._brands.Add(new DisplayMemberModel() { Id = brand.BrandId, DisplayMember = brand.BrandName });
            }
        }
        #endregion
    }
public partial class CtrlBrands : UserControl
    {
        public CtrlBrands()
        {
            InitializeComponent();
            this.DataContext = new CtrlBrandsViewModel();
            this.txtBrandName.Focus();
        }
        private void BtnSave_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            if (btn == null) return;
            //  ToDo: Fehlermeldung anzeigen
            if (this.txtBrandName.Text == null || string.IsNullOrWhiteSpace(this.txtBrandName.Text)) return;
            MainProductPropertyManager.Current.CreateBrand(this.txtBrandName.Text);
        }

        private void BtnDelete_Click(object sender, RoutedEventArgs e)
        {
            IDisplayMemberModel brand = this.lbBrands.SelectedItem as IDisplayMemberModel;
            if (brand == null) return;
            if (this.txtBrandName.Text == null || string.IsNullOrWhiteSpace(this.txtBrandName.Text)) return;
            MainProductPropertyManager.Current.DeleteBrand(brand.Id);
        }

        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ListBox lb = sender as ListBox;
            if (lb == null) return;
            IDisplayMemberModel brand = lb.SelectedItem as IDisplayMemberModel;
            if (brand == null) return;
            this.txtBrandName.Text = brand.DisplayMember;
        }
    }


<UserControl x:Class="ProductManagement.Wpf.Views.CtrlBrands"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ProductManagement.Wpf.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="25" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <StackPanel VerticalAlignment="Top" Grid.ColumnSpan="2" HorizontalAlignment="Center">
            <Label Name="lblBrands" Content="Brands"/>
        </StackPanel>
        <DockPanel Name="dpnlViewBrands" Grid.Row="1">
            <ListBox Margin="15"
                     VerticalAlignment="Stretch"
                     ItemsSource="{Binding Brands}"
                     SelectionChanged="ListBox_SelectionChanged"
                     Name="lbBrands"
                >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <TextBlock Text="{Binding DisplayMember}" Tag="{Binding Id}" />
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
                <ListBox.Style>
                    <Style x:Name="EmptyListStyle"
                           TargetType="ListBox"
                           BasedOn="{StaticResource {x:Type ListBox}}">
                        <Style.Triggers>
                            <Trigger Property="HasItems" Value="false">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate>
                                            <TextBlock Text="Keine Einträge vorhanden" HorizontalAlignment="Center"/>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </ListBox.Style>
            </ListBox>
        </DockPanel>
        <StackPanel Name="dpnlModifyBrands" Grid.Column="1" Grid.Row="1" VerticalAlignment="Bottom">
            <TextBox Name="txtBrandName" VerticalAlignment="Bottom"/>
            <Button Name="btnSave" Content="Speichern" VerticalAlignment="Bottom" Click="BtnSave_Click" />
            <Button Name="btnDelete" Content="Löschen" VerticalAlignment="Bottom" Click="BtnDelete_Click" />
        </StackPanel>
    </Grid>
</UserControl>


Kann mir jemand sagen wo der Fehler liegt bzw. was ich falsch mache?

4.940 Beiträge seit 2008
vor 3 Jahren

Hallo,

du hast eine eigenartige Mischung aus CodeBehind und MVVM, dadurch ist es (für mich) nicht ganz leicht, deinen Code zu verstehen.

Aber ich denke, ich habe dein Problem bzgl. der ListBox gefunden. Du aktualisierst zwar über den MainProductPropertyManager die interne Brand-Liste, aber dadurch wird ja nicht automatisch ObservableCollection<IDisplayMemberModel> Brands aktualisiert (welches ja eine eigenständige Liste ist). Beim Laden in loadBrandItems wird diese ja erst aus der internen Liste erzeugt - und daher müsstes du entweder auch diese Methode aufrufen oder parallel die gebundene ObservableCollection ebenfalls verändern (Add, Delete``).

Und bzgl. Focus informiere dich mal über die Tab Order (und das Element mit dem niedrigsten TabIndex wird dann automatisch selektiert).

T
Taucher Themenstarter:in
307 Beiträge seit 2008
vor 3 Jahren

Hallo Th69,

vielen Dank für deine Antwort.

du hast eine eigenartige Mischung aus CodeBehind und MVVM, dadurch ist es (für mich) nicht ganz leicht, deinen Code zu verstehen.

Nun ja, ich hab halt mein Model, mein ViewModel und meine View. Ich denke es wäre einfacher zu lesen/verstehen, wenn es im VS anzusehen wäre.

Aber zum Problem. Ich habe es jetzt so gelöst, dass ich die Methode loadBrandItems public gemacht habe, und sie dann immer aufrufe, wenn es eine Veränderung gab. Zusätzlich musste ich in der Methode dann noch die Liste von ObservableCollection<IDisplayMemberModel> in der Methode mit Clear() "löschen".

Vielen Dank!

4.940 Beiträge seit 2008
vor 3 Jahren

Ich meinte die Verwendung der Click-Methoden im CodeBehind, anstatt Commands (ICommand) mittels MVVM zu verwenden (dann wäre nämlich die gesamte Logik im ViewModel!).

T
Taucher Themenstarter:in
307 Beiträge seit 2008
vor 3 Jahren

Ich meinte die Verwendung der Click-Methoden im CodeBehind, anstatt Commands (ICommand) mittels MVVM zu verwenden (dann wäre nämlich die gesamte Logik im ViewModel!).

Da hast du recht. Zu diesem Thema komme ich genau jetzt 😉
Wie anfangs schon erwähnt, ich bin recht neu in Wpf und wurschtle mit schrittweise durch.
Dennoch danke für den Hinweis.