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?
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).
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!
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!).
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.