Results 1 to 3 of 3

Thread: WPF Listbox, Adding Checkbox

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Sep 2013
    Posts
    107

    WPF Listbox, Adding Checkbox

    Hi All

    I would like to add a checkbox into a list box. i know how to do this if i know all the items i need and do it through the xaml. Except I'm looking into a user defined excel spreadsheet adding serial numbers and a checkbox I will later deal with.

    I'm not sure if i can. This is what i have so far.
    Code:
        Private Sub Load_Btn_Click(sender As Object, e As RoutedEventArgs) Handles Load_Btn.Click
            Dim dlg As New Microsoft.Win32.OpenFileDialog()
            Dim Xcel As New Microsoft.Office.Interop.Excel.Application
            Xcel.Application.DisplayAlerts = False
    
            dlg.DefaultExt = ".txt"
            dlg.Filter = "Excel Files|*.xls;*.xlsx;*.xlsm"
    
            Dim result As Nullable(Of Boolean) = dlg.ShowDialog()
            If result = True Then
                ' Open document 
                Dim filename As String = dlg.FileName
                Import_FileName = filename
            End If
    
            Try
                Xcel.Application.Workbooks.Open(Import_FileName)
                Dim worksheet As Object = Xcel.Worksheets("MasterComp")
                worksheet.Activate()
    
                SiteName = (worksheet.Range("SiteName").Value)
                SiteID.Text = SiteName
            Catch ex As Exception
                MessageBox.Show("IMPORT FAILED", "Load Site fail", MessageBoxButton.OK, MessageBoxImage.Error)
                Xcel.Quit()
            End Try
            STOP_now = False
    
            Xcel.ActiveSheet.Range("START").Activate()
    
            Try
                Do Until STOP_now = True
                    If Xcel.ActiveCell.Text = "STOP" Then
                        STOP_now = True
                        Exit Do
                    End If
    
                    ' Xcel.ActiveSheet.Activate()
                    SerialBox.Items.Add((Xcel.ActiveCell.Value))
                    Xcel.ActiveCell.Offset(1, 0).Activate()
                Loop
            Catch ex As Exception
                MessageBox.Show("IMPORT FAILED", "Load Site fail", MessageBoxButton.OK, MessageBoxImage.Error)
                MessageBox.Show(ex.Message)
                Xcel.Quit()
            End Try
    
        End Sub
    If this could be done how could you attach a name to it as you go?

    Any help would be good
    Thanks for reading

  2. #2
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,715

    Re: WPF Listbox, Adding Checkbox

    Thread moved from Visual Basic .NET forum to WPF forum.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  3. #3
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: WPF Listbox, Adding Checkbox

    WPF is a little weird until you get the "trick". It's not an old "trick", but WinForms didn't do much to make it obvious or convenient. A ton of people end up spending a whole career without learning it, and that makes me sad because I think about 25% of the questions we field would be easier to answer with this "trick".

    What's the trick? It regards using a type of pattern generally called "Presentation Model". WPF calls its particular flavor "Model-View-ViewModel" or "MVVM" for short. You can read page after page after page about it, but in a nutshell:

    What if, for a random data type, you could just tell Windows, "Display this!", and the UI for that type would just appear? Usually in a ListView or ListBox you're displaying something like a list of Customer types, maybe with a Name and a VIP Boolean. So you've got that type. Now, what if you could just tell the ListBox, "When an item is a Customer, please display a CheckBox assocaited with its "IsVip" property and a TextBox associated with its "Name" property."?

    In Windows Forms, that's really difficult in a ListBox, because your only choices are "display text or draw it yourself". So if you want a CheckBox in a ListBox row, you've got to implement your own CheckBox painting routines, track their state, etc. And Windows Forms doesn't have any built-in way to tie a property to some UI, so you have to handle events and make sure the visual representation of the checkboxes stays in sync with their actual representation.

    In WPF, this is how ListBox is designed to work! ListBox is a "templated control". That means you can provide snippets of XAML inside a DataTemplate and instruct the ListBox to use that UI for particular types. Since a DataTemplate is XAML, it can include data bindings to associate properties of the underlying type with the UI controls. The only trick is for the bindings to be two-way, your underlying type needs to implement the INotifyPropertyChanged interface.

    So let's run with the Customer example. Here's the type that will be used in the ListBox:
    Code:
    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    
    Public Class CustomerViewModel
        Implements INotifyPropertyChanged
    
        Private _isVip As Boolean
        Public Property IsVip As Boolean
            Get
                Return _isVip
            End Get
            Set(value As Boolean)
                _isVip = value
                RaisePropertyChanged()
            End Set
        End Property
    
        Private _name As String
        Public Property Name As String
            Get
                Return _name
            End Get
            Set(value As String)
                _name = value
                RaisePropertyChanged()
            End Set
        End Property
    
        Private Sub RaisePropertyChanged(<CallerMemberName> Optional ByVal propertyname As String = "")
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyname))
        End Sub
    
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    End Class
    The only "tricky" part here is implementing INotifyPropertyChanged. It's tedious, but necessary.

    Now let's have a peek at the XAML:
    Code:
    <Window x:Class="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:WpfApp2"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <ListBox ItemsSource="{Binding Customers}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <CheckBox IsChecked="{Binding IsVip}"
                                      Content="{Binding Name}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>
    If you're only displaying one kind of item, you can put your DataTemplate in the ItemTemplate property. There's a couple of different ways to display many, but I didn't want to complicate the example.

    The ListBox expects to bind to a collection of CustomerViewModel objects via a property named "Customers". For each item, it will display a CheckBox that binds the IsChecked property to the customer's IsVip property and the Content property to the customer's Name property. Now it's our job to make sure the expected properties are present in the BindingContext.

    For that, our window needs a ViewModel. That ViewModel needs a property named "Customers" that is a collection of CustomerViewModel objects. When working with data binding, it's best to use ObservableCollection(Of T), for reasons you can read about in just about any introductory WPF text.
    Code:
    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    
    Public Class MainWindowViewModel
        Implements INotifyPropertyChanged
    
        Private _customers As New ObservableCollection(Of CustomerViewModel)()
        Public ReadOnly Property Customers As ObservableCollection(Of CustomerViewModel)
            Get
                Return _customers
            End Get
        End Property
    
        Private Sub RaisePropertyChanged(<CallerMemberName> Optional ByVal propertyname As String = "")
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyname))
        End Sub
    
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    
    End Class
    I made it ReadOnly because you don't really create a new collection, you add/remove items to the one that exists. ObservableCollection raises events data binding listens for when that happens. That makes INotifyPropertyChanged superfluous here. I implemented it out of habit (generally in any WPF project you end up making some ViewModelBase class that implements it.)

    OK. Now we need to get that connected to the UI, and add some test data. There's some argument in the MVVM community about the "right" way to do that, but there's an "easy" way that's appropriate for an example. In MainWindow.Xaml.vb:
    Code:
    Class MainWindow
    
        Public Sub New()
            InitializeComponent()
    
            Dim vm As New MainWindowViewModel()
            vm.Customers.Add(New CustomerViewModel() With {.Name = "Twilight Sparkle", .IsVip = False})
            vm.Customers.Add(New CustomerViewModel() With {.Name = "Starlight Glimmer", .IsVip = True})
            vm.Customers.Add(New CustomerViewModel() With {.Name = "Lyra Heartstrings", .IsVip = True})
    
            DataContext = vm
        End Sub
    
    End Class
    Every visual element in WPF has a DataContext property. Its data bindings will try to use the properties of its BindingContext. If the property hasn't been set, the element will try to use its parent's. So here's how you end up with UI on the screen:

    When the Window is created, it creates the MainWindowViewModel and adds some customers. When the DataContext property is set, that matches up with the ListBox's binding from ItemsSource -> Customers. So now the ListBox has to display every CustomerViewModel in the list. To do that, it creates the UI in its ItemTemplate for each item, and sets that UI's BindingContext to the specific CustomerViewModel it's bound to. When that happens, each of those bits of UI binds to those properties.

    So the end result of all that code is a ListBox that displays a CheckBox for each CustomerViewModel. There's a lot of little tweaks if you don't want selection highlights, but those are easy to find via searches.

    So, in terms of your project, assuming you already have a Window with a ListBox:
    • You need to create an object (that implements INotifyPropertyChanged) that represents whatever the Excel row represents, with a property for each thing you care about.
    • You need to create a DataTemplate with UI and bindings for that object, and make sure the ListBox knows to use it.
    • You need to bind the ListBox's ItemsSource property to an ObservableCollection of those objects.
    • When you read the Excel file, for each row create one of those objects and add it to the ObservableCollection.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width