1
votes

I find the problem I have very strange.

I have a WPF application using MVVM pattern.

I retrieve data from my database using Linq-to-SQL and display the Description field data in a ListBox using binding.

It works, but not completely. For some reason the last record's does not display. I have verified it is fetched from the database. It cannot be selected; its not there at all.

The only way to get the record to appear is by setting focus to the ListBox and either scrolling the mouse scroller or actually moving down using the keyboard to past the last record (which is supposed to be the second last record). Once you do that, the record appears.

Another way to get the record to show up is update any of the records in the ListBox. I have a TextBox bound in two-way mode to the SelectedItem of the ListBox, So when I select an item, change the text in the TextBox and click the Update button, it calls the Command to do the database update, which works fine, but then also the missing record of the last record shows up.

Is there a rendering problem with ListBox and ListView, because I have tried both? Why does it seem like the ListBox needs to be "redrawn" before the last items appears?

CategoryModel.cs

public class CategoryModel
{
    public int CategoryID { get; set; }
    public string Description { get; set; }

    public List<CategoryModel> categories = new List<CategoryModel>();
    readonly SalesLinkerDataContext _dbContext = new SalesLinkerDataContext();

    public void GetCategories()
    {
        categories.Clear();

        var result = _dbContext.tblSalesCategories.ToList();

        foreach (var item in result)
        {
            categories.Add(new CategoryModel
            {
                CategoryID = item.CategoryID,
                Description = item.Description.Trim()
            });
        }
    }

    internal void Update(CategoryModel cm)
    {
        try
        {
            var category = (from a in _dbContext.tblSalesCategories
                where a.CategoryID == cm.CategoryID
                select a).FirstOrDefault();

            if (category != null)
            {
                category.Description = cm.Description;
                _dbContext.SubmitChanges();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

CategoriesViewModel.cs

public class CategoriesViewModel : ViewModelBase, IPageViewModel
{
    public CategoryModel Categories = new CategoryModel();

    private DelegateCommand _getCategoriesCommand;
    private DelegateCommand _updateCategoryCommand;


    /// <summary>
    /// Describes the name that will be used for the menu option
    /// </summary>
    public string Name
    {
        get { return "Manage Categories"; }  

    }

    public string Description
    {
        get { return Categories.Description; }
        set
        {
            Categories.Description = value;
            OnPropertyChanged("Description");
        }
    }

    public List<CategoryModel> ReceivedCategories
    {
        get { return Categories.categories; }

        set
        {
            Categories.categories = value;
            OnPropertyChanged("ReceivedCategories");
        }
    }

    public ICommand GetCategoriesCommand
    {
        get
        {
            if (_getCategoriesCommand == null)
            {
                _getCategoriesCommand = new DelegateCommand(GetCategories, CanGetCategories);
            }

            return _getCategoriesCommand;
        }   
    }

    private bool CanGetCategories()
    {
        return true;
    }

    private void GetCategories()
    {
        Categories.GetCategories();
        ReceivedCategories = Categories.categories;

    }

    public ICommand UpdateCategoryCommand
    {
        get
        {
            if (_updateCategoryCommand == null)
            {
                _updateCategoryCommand = new DelegateCommand(UpdateCategory, CanUpdateCategory);
            }

            return _updateCategoryCommand;
        }


    }

    private bool CanUpdateCategory()
    {
        return true;
    }

    private void UpdateCategory()
    {

        Categories.Update(Categories);
    }

}

CategoriesView.xaml

<UserControl x:Class="SalesLinker.CategoriesView"
  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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
  mc:Ignorable="d" 
  d:DesignHeight="300" d:DesignWidth="600" Background="White" >

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding GetCategoriesCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

<Grid >
    <Grid.RowDefinitions>
        <RowDefinition Height="45"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="250"/>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Label Grid.Row="0" Grid.Column="0" Margin="20,0,0,0" FontSize="20" HorizontalAlignment="Center" Content="Categories"/>
    <ListView x:Name="LstCategories" ItemsSource="{Binding ReceivedCategories, Mode=TwoWay}" Grid.Column="0" Grid.Row="1" 
              VerticalAlignment="Stretch"
              Background="LightGray"
              ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
              SelectionChanged="LstCategories_OnSelectionChanged">

        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" >
                    <TextBlock Text="{Binding Description }"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

    <Button Command="{Binding AddCategoryCommand}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" Height="50" Width="50" Margin="0,20,0,0" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" >
        <Image Source="/Images/Plus.png"/>
    </Button>
    <Button Command="{Binding RemoveCategoryCommand}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" Height="50" Width="50" Margin="0,75,0,0" Background="Transparent" BorderThickness="0">
        <Image Source="/Images/Minus.png"/>
    </Button>

    <Grid Grid.Row="1" Grid.Column="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="75"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Label VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="0" Content="Description:"/>

        <TextBox DataContext="CategoryModel" Grid.Row="0" Grid.Column="1" Width="250" Height="Auto" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0" 
                 Text="{Binding SelectedItem.Description, ElementName=LstCategories}"/>


        <Button Command="{Binding UpdateCategoryCommand}" 
                Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Margin="10,0,0,0" Height="20" Width="120" Content="Update Description"/>

    </Grid>
</Grid>

I am very new to MVVM so hopefully I am just not seeing something simple.

1
Code looks OK. Try temporary exclude usage of _dbContext: generate the fake list with known elements count and look at result.nicolay.anykienko
Same result, last item doesn't show.Neill

1 Answers

0
votes

For some reason the last record's does not display. I have verified it is fetched from the database.

If your fetch all data from database, then it can be concluded that why you cannot see your last item is as you use using not correct layout. I mean static layout.

I suggest you to use dynamic layout, not static. I mean it is bad:

<Grid.RowDefinitions>
    <RowDefinition Height="45"/>
    <RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="250"/>
    <ColumnDefinition Width="100"/>
    <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

The following code is better and it affords you to see all items to be seen and to be resized accoriding the display and Width and Height of your Window or UserControl:

   <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>

Let me show the full example:

<Grid>
  <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition Width="2*"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    <Label Grid.Row="0" Grid.Column="0" Margin="20,0,0,0" FontSize="20" HorizontalAlignment="Center" Content="Categories"/>
    <ListView x:Name="LstCategories" ItemsSource="{Binding ReceivedCategories, Mode=TwoWay}" Grid.Column="0" Grid.Row="1" 
          VerticalAlignment="Stretch"
          Background="LightGray"
          ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" >
                    <TextBlock Text="{Binding Description }"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>

    </ListView>

    <Button Command="{Binding AddCategoryCommand}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" Height="50" Width="50" Margin="0,20,0,0" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" >
        <Image Source="/Images/Plus.png"/>
    </Button>
    <Button Command="{Binding RemoveCategoryCommand}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" Height="50" Width="50" Margin="0,75,0,0" Background="Transparent" BorderThickness="0">
        <Image Source="/Images/Minus.png"/>
    </Button>

    <Grid Grid.Row="1" Grid.Column="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1.5*"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>

        <Label VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="0" Content="Description:"/>

        <TextBox DataContext="CategoryModel" Grid.Row="0" Grid.Column="1" Height="Auto" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0" 
             Text="{Binding SelectedItem.Description, ElementName=LstCategories}"/>


        <Button Command="{Binding UpdateCategoryCommand}" 
            Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Content="Update Description"/>
</Grid>

Update:

In order to dispel any doubts, I've made a test to show that all items are shown:

I've made some test model class:

public class FooClass
{
    public string Description { get; set; }
}

And I've populated your ListView in a loop:

public MainWindow()
{
   InitializeComponent();
   PopulateCollection();
}

private void PopulateCollection()
{
   List<FooClass> fooColl = new List<FooClass>();
   for (int i = 0; i <= 1000; i++)
   {
      fooColl.Add(new FooClass() { Description=i.ToString()});
   }
   LstCategories.ItemsSource = fooColl;       
}

XAML:

<ListView x:Name="LstCategories" Grid.Column="0" Grid.Row="1"
    VerticalAlignment="Stretch"
      Background="LightGray"
      ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" >
                    <TextBlock Text="{Binding Description }"/>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>

 </ListView>

The result is:

enter image description here