Author Topic: WPF bind a Combobox  (Read 1723 times)

0 Members and 1 Guest are viewing this topic.

djee

  • Newt
  • Posts: 49
WPF bind a Combobox
« on: November 23, 2016, 06:00:39 PM »
I've created a custom class (Autocad template class)
Code: [Select]
Imports System.Collections.ObjectModel
Imports Autodesk.AutoCAD.DatabaseServices

Namespace Model
    Public Class TemplateFile
        Public Property SelectedTemplateFileName() As String
        Public Property SelectedTemplateFilePath() As String
        Private _templateLayoutName As ObservableCollection(Of String)

        Public Sub New()
        End Sub

        Public ReadOnly Property TemplateLayoutNameList() As ObservableCollection(Of String)
            Get
                Return ListLayoutName(SelectedTemplateFilePath)
            End Get
        End Property

        ''' <summary>
        ''' this function return a list of all layout name (as string) on an external drawing
        ''' </summary>
        ''' <param name="filename"></param>
        ''' <returns></returns>
        Private Shared Function ListLayoutName(ByVal filename As String)
            Dim layoutName As ObservableCollection(Of String) = New ObservableCollection(Of String)
            ' Create a new database object and open the drawing into memory
            Dim acExDb As Database = New Database(False, True)
            acExDb.ReadDwgFile(filename, FileOpenMode.OpenForReadAndAllShare, True, "")
            ' Create a transaction for the external drawing
            Using acTransEx As Transaction = acExDb.TransactionManager.StartTransaction()
                ' Get the layouts dictionary
                Dim layoutsEx As DBDictionary = acTransEx.GetObject(acExDb.LayoutDictionaryId, OpenMode.ForRead)
                For Each entry As DBDictionaryEntry In layoutsEx
                    layoutName.Add(entry.Key)
                Next

            End Using
            Return layoutName
        End Function
    End Class
End Namespace

& I would like to bind an instance of the class to a ComboBox (Xaml below). My path=TemplateLayoutNameList, source=_selectTemplate & target=TemplateLayoutNameComboBox
Code: [Select]
<ComboBox x:Name="TemplateLayoutNameComboBox"
                  HorizontalAlignment="Left"
                  ItemsSource="{Binding TemplateLayoutNameList, UpdateSourceTrigger=PropertyChanged}"
                  Margin="10,10,0,10"
                  Grid.Row="8"
                  Width="401" Grid.ColumnSpan="2"
                  Grid.Column="0" />

& here is my code behind
Code: [Select]
Public Class Window1
Private Shared _selectTemplate As TemplateFile

    Private Sub Window_Loaded(sender As Object, e As Windows.RoutedEventArgs)
        _selectTemplate = New TemplateFile()
    End Sub


Private Sub button_Click_1(sender As Object, e As Windows.RoutedEventArgs) Handles button.Click
  'browse for the files to be processed...
        Dim openFileDialog As New OpenFileDialog()
        openFileDialog.Multiselect = False
        openFileDialog.Filter = "AutoCAD files (*.dwg)|*.dwg|Drawing Template (*.dwt)|*.dwt|All files (*.*)|*.*"
        openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
        If openFileDialog.ShowDialog() = True Then
            _selectTemplate.SelectedTemplateFileName = Path.GetFileName(openFileDialog.FileName)
            _selectTemplate.SelectedTemplateFilePath = Path.GetFullPath(openFileDialog.FileName)
        End If


'set binding for combobox template layout
        Dim bin As New Binding("TemplateLayoutNameList")
        bin.Mode = BindingMode.OneWay
        bin.Source = _selectTemplate
        TemplateLayoutNameComboBox.DataContext = _selectTemplate
        BindingOperations.SetBinding(TemplateLayoutNameComboBox, ComboBox.TextProperty, bin)
End Sub
End Class

So when I excute this, the combobox get filled the first time (good) but it does not update for the later iteration. I know i'm doing this all the wrong way... Basically, I would like to bind the combobox to a read-only property (collection). Anyone could point me in the right direction...

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: WPF bind a Combobox
« Reply #1 on: November 24, 2016, 04:01:09 AM »
Hi,

The layouts list does not need to be observable (it won't change), but the code behind class must implement INotifyPropertyChanged so that the "TemplateLayoutNameList" property can notify the window it changed.

Here's a minimalist implementation.

XAML
Code - XML: [Select]
  1. <Window x:Class="TemplateLayoutListSample.Dialog"
  2.             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5.             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6.             xmlns:local="clr-namespace:TemplateLayoutListSample"
  7.             mc:Ignorable="d"
  8.             Title="Dialog" Height="170" Width="350" MinHeight="170" MaxHeight="170" MinWidth="250" WindowStartupLocation="CenterOwner">
  9.     <Grid>
  10.         <Grid.RowDefinitions>
  11.             <RowDefinition Height="*"/>
  12.             <RowDefinition Height="Auto"/>
  13.         </Grid.RowDefinitions>
  14.         <Grid Grid.Row="0">
  15.             <Grid.RowDefinitions>
  16.                 <RowDefinition Height="Auto"/>
  17.                 <RowDefinition Height="Auto"/>
  18.                 <RowDefinition Height="Auto"/>
  19.             </Grid.RowDefinitions>
  20.             <Button Grid.Row="0" Margin="5" Width="80" Click="Browse_Click">Browse...</Button>
  21.             <TextBlock Grid.Row="1" Margin="5" Text="{Binding FileName}" />
  22.             <ComboBox Grid.Row="2" Margin="5" HorizontalAlignment="Stretch" ItemsSource="{Binding Layouts}" SelectedItem="{Binding Layout}" />
  23.         </Grid>
  24.         <Grid Grid.Row="1">
  25.             <Grid.ColumnDefinitions>
  26.                 <ColumnDefinition/>
  27.                 <ColumnDefinition/>
  28.             </Grid.ColumnDefinitions>
  29.             <Button Grid.Column="0" Width="80" Margin="10" HorizontalAlignment="Right" Click="OK_Click">OK</Button>
  30.             <Button Grid.Column="1" Width="80" Margin="10" HorizontalAlignment="Left" IsCancel="True">Annuler</Button>
  31.         </Grid>
  32.     </Grid>
  33. </Window>

Code behind
Code - C#: [Select]
  1. using Autodesk.AutoCAD.DatabaseServices;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Windows;
  5.  
  6. namespace TemplateLayoutListSample
  7. {
  8.     public partial class Dialog : Window, INotifyPropertyChanged
  9.     {
  10.         #region INotifyPropertyChanged implementation
  11.         public event PropertyChangedEventHandler PropertyChanged;
  12.  
  13.         public void RaisePropertyChanged(string propertyName)
  14.         {
  15.             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  16.         }
  17.         #endregion
  18.  
  19.         private IEnumerable<string> layouts;
  20.         private string fileName, layout;
  21.  
  22.         public string FileName
  23.         {
  24.             get { return fileName; }
  25.             private set
  26.             {
  27.                 fileName = value;
  28.                 RaisePropertyChanged("FileName");
  29.                 Layouts = GetLayouts();
  30.                 Layout = "Model";
  31.             }
  32.         }
  33.  
  34.         public IEnumerable<string> Layouts
  35.         {
  36.             get { return layouts; }
  37.             private set { layouts = value; RaisePropertyChanged("Layouts"); }
  38.         }
  39.  
  40.         public string Layout
  41.         {
  42.             get { return layout; }
  43.             set { layout = value;  RaisePropertyChanged("Layout"); }
  44.         }
  45.  
  46.         public Dialog()
  47.         {
  48.             InitializeComponent();
  49.             DataContext = this;
  50.             FileName = HostApplicationServices.WorkingDatabase.Filename;
  51.         }
  52.  
  53.         private void Browse_Click(object sender, RoutedEventArgs e)
  54.         {
  55.             var openFileDialog = new Microsoft.Win32.OpenFileDialog();
  56.             openFileDialog.Filter = "AutoCAD files (*.dwg)|*.dwg|Drawing Template (*.dwt)|*.dwt";
  57.             var result = openFileDialog.ShowDialog();
  58.             if (result.HasValue && result.Value)
  59.             {
  60.                 FileName = openFileDialog.FileName;
  61.             }
  62.         }
  63.  
  64.         private void OK_Click(object sender, RoutedEventArgs e)
  65.         {
  66.             DialogResult = true;
  67.         }
  68.  
  69.         private IEnumerable<string> GetLayouts()
  70.         {
  71.             using (var db = new Database(false, true))
  72.             {
  73.                 db.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndAllShare, false, null);
  74.                 using (var tr = db.TransactionManager.StartOpenCloseTransaction())
  75.                 {
  76.                     var layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
  77.                     foreach (var entry in layoutDict)
  78.                     {
  79.                         yield return entry.Key;
  80.                     }
  81.                 }
  82.             }
  83.         }
  84.     }
  85. }
  86.  

Test command
Code - C#: [Select]
  1. using Autodesk.AutoCAD.Runtime;
  2. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  3.  
  4. [assembly: CommandClass(typeof(TemplateLayoutListSample.Commands))]
  5.  
  6. namespace TemplateLayoutListSample
  7. {
  8.     public class Commands
  9.     {
  10.         [CommandMethod("Test")]
  11.         public void Test()
  12.         {
  13.             Dialog dialog = new Dialog();
  14.             bool? result = AcAp.ShowModalWindow(dialog);
  15.             if (result.Value)
  16.             {
  17.                 AcAp.ShowAlertDialog(dialog.FileName + "\n" + dialog.Layout);
  18.             }
  19.         }
  20.     }
  21. }
  22.  
Speaking English as a French Frog