WPF - binding controls to array elements


  • Banned

    I have a fixed-size array of enums in my ViewModel, and I want to bind each element to its own ComboBox. I'm not sure how to do it correctly. I tried:

    • Binding each to array property, and indexing in binding path. Except this way, only getter will get called, not setter.
    • Using ObservableCollection instead of plain array. Except I can't use indexing now, and I can't bind to expressions.
    • Making separate properties for each element. Except it leads to lots of code repetition. I thought of using T4 template, but after I saw all those security warning dialogs and lack of syntax highlighting, I started to doubt if T4 templates are a good (as in best practices) solution for anything at all.

    What should I do?


  • Banned

    75 views and 0 replies. Seriously, is there really nobody on this forum who ever used arrays in WPF?


  • ♿ (Parody)

    @gąska said in WPF - binding controls to array elements:

    Seriously, is there really nobody on this forum who ever used arrays in WPF?

    Sorry, I wouldn't even know what WPF stands for without searching.


  • Impossible Mission - B

    @boomzilla Wildly Puzzling Framework. It's a new and "improved" .NET UI technology that replaced the perfectly adequate, stable, and easily-understood *ahem* badly outdated and soooo last decade WinForms with a big mess of XML so complicated it's been described as having a learning cliff rather than the usual learning curve.



  • @gąska I used to do WPF. I remember using ObservableCollections. Or maybe even I made my own collection that implements the change notification interface.

    But it's been years, I forgot all the details.

    Surely this shouldn't be a huge problem?



  • What'd you bind to? ItemsSource would normally work I would think... I'll go mess around with that.

    EDIT: Whoa, I see what you're trying to do now... I'm horrified, but I'll try it I guess...



  • I like WPF. I'll try.



  • Okay, so, if I'm reading you right, you want basically this?

    <ItemsControl ItemsSource="{Binding Whatevers}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <ComboBox ItemsSource="{Binding}"/>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    The issue you'll face here, of course, is binding the selected item of each, since in this example we've just templated it. I'd probably make your array be of viewmodels that contain a property with the array and a property of the selected value.

    I like WPF a lot, but yeah, this is kind of an odd situation.

    I'll additionally note that there's a way to make the IDE recognize the type the datatemplate refers to and therefore allow completion to work and the properties to be recognized before runtime, but I don't remember how. It's been like a year since I've been able to actually work on WPF.



  • @masonwheeler said in WPF - binding controls to array elements:

    WinForms

    What are those? We like WTL here...



  • @dcon Windows forms are great for rapidly getting some UI up and running, though I think I know WPF thoroughly enough that the difference is minor for me. WPF, once you get what they're going for, can do amazing things really easily. I just wish they'd hook up the newer XAML version that has types and generics...



  • @magus I do know what they are :) (even if I've never used them). The odds of moving to any of them are between slim and none.



  • @magus said in WPF - binding controls to array elements:

    once you get what they're going for

    My experience was "I've been waiting for exactly this" when I first saw WPF.
    Mind you, my previous GUI experience was Java Swing and Android, and all three in university courses.


  • Impossible Mission - B

    @marczellm I'm so sorry...


  • Winner of the 2016 Presidential Election

    @marczellm said in WPF - binding controls to array elements:

    @magus said in WPF - binding controls to array elements:

    once you get what they're going for

    My experience was "I've been waiting for exactly this" when I first saw WPF.
    Mind you, my previous GUI experience was Java Swing and Android, and all three in university courses.

    That revelation was followed shortly after by "Some of this stuff is way too needlessly complicated" in my case.


  • Banned

    @magus said in WPF - binding controls to array elements:

    The issue you'll face here, of course, is binding the selected item of each, since in this example we've just templated it. I'd probably make your array be of viewmodels that contain a property with the array and a property of the selected value.

    So, if I understood properly, I should do something like:

    public class WindowVM: BindableBase // using Prism
    {
        public class ComboBoxVM: BindableBase
        {
            public static Foo[] ItemSource { get; } = ...;
            public Foo Selected { get; set; } = ...;
        }
        public ComboBoxVM[] ComboBoxVMs { get; }
    }
    

    And then bind individual ComboBoxes to these objects? How do I do it, exactly?

    Also. Converters are mostly single-use classes that are very lengthy to define. Is there some library of common converters, or is copy-pasting from StackOverflow the way to go? (I saw a lot of people copy-pasting RelayCommand and ViewModelBase around, then found Prism. Bug it doesn't have converters.)



  • @gąska That'd be an effective way to do it. You could probably do it all with anonymous types, except that the Selected property really needs to notify property changed. The binding ought to be like I showed above, except that in the combobox, you do <ComboBox ItemsSource="{Binding ItemsSource}" Selected="{Binding Selected}" ....

    Alternatively, if they all use the same options, you could bind them all to the same collection for that with different properties for each selected, or whatever else. It's pretty easy to deal with.

    As for converters, they're kind of a pain. I typically just define the ones I need. You can, if you want, define properties that you can set when defining a converter instance, and use those in the conversion in some way, but overall I'd tend more toward making them single-use and understandable. I don't know about libraries for them, but they really ought to exist.

    I personally do write my own DelegateCommand (similar to RelayCommand probably) in my own projects, and sometimes something like ViewModelBase, because I don't even need a quarter of Prism (I have a thread here about using MEF for lots of prismy things).

    As to the general case of binding combo boxes to collections like this, if you do it often you may want to make a style, and override the templates for both the items and the itemscontainer, but if this is a one-off, just go ahead and do it in place.


  • Banned

    @magus said in WPF - binding controls to array elements:

    The binding ought to be like I showed above, except that in the combobox, you do <ComboBox ItemsSource="{Binding ItemsSource}" Selected="{Binding Selected}" ....

    I mean, where do I say in XAML that "no, don't bind these bindings to the window's viewmodel, but rather to your own little viewmodel that's a member of the main viewmodel that you can access via..."?

    @magus said in WPF - binding controls to array elements:

    I personally do write my own DelegateCommand (similar to RelayCommand probably) in my own projects, and sometimes something like ViewModelBase, because I don't even need a quarter of Prism (I have a thread here about using MEF for lots of prismy things).

    I'm also using it to bind events to commands (InvokeCommandAction). And I'm not rewriting this piece. So if I'm stuck with this dependency, let's use it.

    @magus said in WPF - binding controls to array elements:

    As to the general case of binding combo boxes to collections like this, if you do it often you may want to make a style, and override the templates for both the items and the itemscontainer, but if this is a one-off, just go ahead and do it in place.

    I don't use template, but I do use style. Not sure what's the difference between two (they seem to cover pretty much the same functionality), and I already know the syntax of the latter.


  • Banned

    @magus is changing DataSource of child controls OK or will it open a can of worms similar to one I opened when I tried to add null to ComboBox's items?



  • @gąska said in WPF - binding controls to array elements:

    I mean, where do I say in XAML that "no, don't bind these bindings to the window's viewmodel, but rather to your own little viewmodel that's a member of the main viewmodel that you can access via..."?

    You don't have to. If you use an ItemsControl like I did above, each item's data bindings apply to their item in the ItemsSource collection bound to the control. It's automatic.

    @gąska said in WPF - binding controls to array elements:

    I don't use template, but I do use style. Not sure what's the difference between two (they seem to cover pretty much the same functionality), and I already know the syntax of the latter.

    A style lets you save specific values for properties and then apply that style to multiple things. The template of a control is what it actually visibly looks like: Any control can be completely overridden into anything you want with templates. Normally, messing with them too much is dangerous, because you risk your app looking weird, but an ItemsControl is essentially just there to be extended, and you can do all you need with one of those, just by applying an ItemTemplate with a style and maybe an ItemsContainerTemplate, if you want them to go horizontally or have spacing between them.


  • Banned

    @magus said in WPF - binding controls to array elements:

    @gąska said in WPF - binding controls to array elements:

    I mean, where do I say in XAML that "no, don't bind these bindings to the window's viewmodel, but rather to your own little viewmodel that's a member of the main viewmodel that you can access via..."?

    You don't have to. If you use an ItemsControl like I did above, each item's data bindings apply to their item in the ItemsSource collection bound to the control. It's automatic.

    Oh, I see. Your ItemsControl repeatedly created ComboBox for each item in ItemsSource. That's not what I wanted.

    Anyway. I finally managed to make something that seems to work. I wonder how many conventions I broke with this code (I definitely broke Geneva Conventions).

            <Grid.Resources>
                <Style x:Key="wireComboBox" TargetType="ComboBox">
                    <Setter Property="Width" Value="70"/>
                    <Setter Property="Height" Value="Auto"/>
                    <Setter Property="Margin" Value="5"/>
                    <Setter Property="ItemsSource" Value="{Binding ColorsList}"/>
                    <Setter Property="SelectedValue" Value="{Binding Wire}"/>
                </Style>
            </Grid.Resources>
            <Label Grid.Row="0" Grid.Column="0" Margin="5" HorizontalAlignment="Center" Content="Wires:"/>
            <ComboBox Grid.Row="1" Grid.Column="0" Style="{StaticResource wireComboBox}" DataContext="{Binding Wires[0]}"/>
            <ComboBox Grid.Row="2" Grid.Column="0" Style="{StaticResource wireComboBox}" DataContext="{Binding Wires[1]}"/>
            <ComboBox Grid.Row="3" Grid.Column="0" Style="{StaticResource wireComboBox}" DataContext="{Binding Wires[2]}"/>
            <ComboBox Grid.Row="4" Grid.Column="0" Style="{StaticResource wireComboBox}" DataContext="{Binding Wires[3]}"/>
            <ComboBox Grid.Row="5" Grid.Column="0" Style="{StaticResource wireComboBox}" DataContext="{Binding Wires[4]}"/>
            <ComboBox Grid.Row="6" Grid.Column="0" Style="{StaticResource wireComboBox}" DataContext="{Binding Wires[5]}"/>
    


  • @gąska Why would you not let the itemscontrol make the comboboxes? You should be able to make the container template into a vertical stackpanel and get the same result as you're getting here, with simpler code.


  • Banned

    @magus I'm using grid's second column for labels that have to match comboboxes in first column. For me, a grid seems like a better solution than a horizontal stackpanel of vertical stackpanel of horizontal stackpanels.

    Also. ItemsControl is a base class of all controls that contain multiple data items. How idiomatic is using it for generating multiple controls? Because it totally looks like a horrible hack and an abuse of framework, considering templates are supposed to define looks and not content.



  • @gąska Itemscontrols are absolutely the base of other things, but specifically unimplemented and usable specifically so you can do things like this. If you used, say, a listview or listbox, you'd get selection highlights.

    It specifically exposes templates for content presentation (data templates exist for the very purpose of displaying specific data types in this sort of context).

    And there's no reason you can't put the entire control into a grid cell.

    WPF allows a lot of freedom, and that can certainly let you shoot yourself in the foot, but I will pretty much always err on the side of defining something once and letting the rest be generated, rather than repeat myself.



  • @gąska said in WPF - binding controls to array elements:

    Also. ItemsControl is a base class of all controls that contain multiple data items. How idiomatic is using it for generating multiple controls? Because it totally looks like a horrible hack and an abuse of framework, considering templates are supposed to define looks and not content.

    I totally see ItemsControl as the Mother Of All Foreach.



  • @marczellm It basically is how you foreach your UI, indeed.


  • Notification Spam Recipient

    @gąska said in WPF - binding controls to array elements:

    75 views and 0 replies.

    Sorry, my available processing time allocation towards the forum has greatly decreased in recent times.

    My closest suggestion (which may be solved already) would involve creating a class that has properties that access those specific array slots in a manner similar to your third idea. Unless you're going to be doing this a lot, it might be easier to just one-and-done it.


  • Banned

    @tsaukpaetra I hope you at least read the rest of the thread after posting. Because reading before posting is clearly too much.


  • Notification Spam Recipient

    @gąska said in WPF - binding controls to array elements:

    Also. ItemsControl is a base class of all controls that contain multiple data items. How idiomatic is using it for generating multiple controls? Because it totally looks like a horrible hack and an abuse of framework, considering templates are supposed to define looks and not content.

    Oh, that reminds me, I totally actually used a StackOverflow answer to do exactly this for my logging window thing, which consists of a list of log objects, basically a item of four textboxes bound to four properties. Worked pretty well, all things considered...


  • Notification Spam Recipient

    @gąska said in WPF - binding controls to array elements:

    @tsaukpaetra I hope you at least read the rest of the thread after posting. Because reading before posting is clearly too much.

    You're lucky I caught this thread before it reached the hundreds of posts!

    That's happened, you know.

    Edit: Also, welcome to the forums!



  • Honestly, a hybrid approach ought to work well:

    C#:

    public class ComboBoxVM : BindableBase
    {
      public Foo Selected { get; set; }...
      public string Label { get; set; }...
    }
    
    ...
    
    public class YourViewModel
    {
      public Foo[] Wires { get; }...
      public ComboBoxVM[] ComboBoxViewModels { get; }
    }
    

    XAML:

    <Label Grid.Row="0" Grid.Column="0" Margin="5" HorizontalAlignment="Center" Content="Wires:"/>
    <ItemsControl Grid.Row="1" ItemsSource="{Binding ComboBoxViewModels}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto"/> <!--​ maybe a set width, though -->
              <ColumnDefinition Width="5px"/>
              <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <ComboBox Grid.Column="0" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=YourView} Path=DataContext.Wires}" Selected="{Binding Selected}"/>
            <TextBlock Text="{Binding Label}"/>
          </Grid>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    What you'll get, then, is all the combo boxes next to labels, and they should all be perfectly aligned, assuming the comboboxes are the same width. There's one super hacky looking binding if you do it this way, because it has to reach up and grab the outer view. I may have even gotten that wrong, since it's been forever. You could also just give some parent a key at a level far enough out for the template to touch, and bind to that thing's view model.

    Honestly, I'm confused about why you have 5 different collections of the same enums, though maybe I'm misunderstanding and they actually aren't just copies of the same collection.

    And I'm not saying do it this way: just saying you have more options than you could possibly imagine.

    But nothing, in my opinion, is as hacky as binding in your styles ;)


  • Banned

    @magus said in WPF - binding controls to array elements:

    Honestly, I'm confused about why you have 5 different collections of the same enums, though maybe I'm misunderstanding and they actually aren't just copies of the same collection.

    ColorsList there is a static property created by enumerating every option of an enum. If I knew how to do this without creating a static property to bind to, I'd do it without a static property to bind to.

    @magus said in WPF - binding controls to array elements:

    But nothing, in my opinion, is as hacky as binding in your styles

    Isn't that what styles are for? Set properties to the same values over and over again?


  • Notification Spam Recipient

    @gąska said in WPF - binding controls to array elements:

    Isn't that what styles are for? Set properties to the same values over and over again?

    I may be :whoosh: ing, but the name "styles" doesn't imply repeating values to me... Maybe consistent application of template form, but if you're making a new style for each and every control maybe you'd be better off just setting that on the control directly?



  • @gąska A style is for applying something to a number of things, sure, but it's a cascading style like in CSS. You've accessed the style by key, so that shouldn't be a problem, but typically I don't like my styles to depend on my datacontext.

    DataTemplate is a specific template type that is meant to be the way to display a specific data type when that data type is encountered.



  • @gąska said in WPF - binding controls to array elements:

    ColorsList there is a static property created by enumerating every option of an enum. If I knew how to do this without creating a static property to bind to, I'd do it without a static property to bind to.



  • @marczellm That's actually rather cool; I didn't know that was possible.


  • Banned

    @marczellm except I want to prepend the list with null. So the property looks like this:

                public static IEnumerable<Box<Color?>> ColorsList { get; } =
                    new Color?[] { null }
                    .Concat(Enum.GetValues(typeof(Color)).Cast<Color?>());
    

    Actually, it's even worse because I've noticed that if you have null to ItemsSource, everything goes to shit. So I made a wrapper class that overrides ToString() for null, and the property actually looks like this:

                public static IEnumerable<Box<Color?>> ColorsList { get; } =
                    new Color?[] { null }
                    .Concat(Enum.GetValues(typeof(Color)).Cast<Color?>())
                    .Select(i => new Box<Color?>(i));
    

    Ain't gonna build this shit in XAML.



  • @gąska said in WPF - binding controls to array elements:

    except I want to prepend the list with null

    Yep I ran into that. I'm sorry for you.


Log in to reply