سؤال

WPF/VC++ (C++/cx) - Visual Studio 2013

So I have a ListBox thats populated with a UserControl I've created. Every time the page changes the data is lost, which is unfortunate. So I have the option of loading the data into the ListBox every time the page is loaded or passing the data around from page to page. They each have their own advantage and disadvantage but is there a practice that is preferred?

I suppose the things to think about would be how many of the UserControls are in the ListBox, which could be a large amount (large being 10000+), and how the data is stored. For now I'm storing the data in a AVLTree, mostly for the organization so that sorting doesn't need to be done. There's only 10 pages, 6 of which would normally be navigated to (4 of them being pages that are menus that are rarely used or only used once) so the data wouldn't have to be passed from page to page often. The size of each data node is only about ~1mb, so not very large but large enough to make a difference as you start getting past 10000 entries (which could be considered a rare circumstance).

I come from game programming so speed and efficiency is a primary goal.

Yes, it is a subjective question, however it is possible for there to be a preferred method among professionals or the general programming community.

هل كانت مفيدة؟

المحلول

Without any more background information, nor details about what you're doing (since you can't post any code or screenshots), I can only tell you what's the basic idea behind The WPF Mentality.

WPF is really different from pretty much any other UI frameworks I've heard of, in the sense that it is really intended to be used for Data-Centric development.

From the ground up, WPF is largely based on DataBinding. This eliminates in like 95% the need to do any manipulations of the UI via procedural code.

As a simple example, say you have a Person class which contains string LastName and string FirstName properties:

public class Person
{
    public string LastName {get;set;}
    public string FirstName {get;set;}
}

Instead of passing data values back and forth to the UI by writing procedural code-behind like this:

//Traditional approach, Don't use this in WPF.
this.txtLastName.Text = person.LastName;
this.txtFirstName.Text = person.FirstName;
//...
person.LastName = this.txtLastName.Text;
person.FirstName = this.txtFirstName.Text;

you simply define a DataBinding in XAML, declaratively:

<TextBox Text="{Binding LastName}"/>
<TextBox Text="{Binding FirstName}"/>

and then set the UI's DataContext to a relevant instance of data:

//Window constructor example
public MainWindow()
{
    InitializeComponent();
    DataContext = new Person();
}

This approach has the following advantages:

  • it clearly Separates UI from Data, allowing a huge amount of scalability due to the independence of these layers. You can put literally ANYTHING in the UI without having to change a single line of code from your application logic / business logic.
  • It reduces boilerplate because WPF takes care of passing data In Both Directions (Data <=> UI).
  • It allows for a property-based approach (as opposed to an event-based one). For example, there is no need for things like handling the TextBox.TextChanged event. WPF takes care of passing the Text value from the TextBoxes in the example to the underlying Data Object when needed.
  • It removes the need to navigate the Visual Tree. The WPF Visual Tree is a really complex structure, which has complex state changes and complex transitions and a complex lifecycle. You really Don't want to have to deal with that in order to, say, "populate a couple of TextBoxes".
  • When dealing with Collections, it helps leverage WPF's UI Virtualization capabilities in a natural way (you don't actually have to do anything it's enabled by default). This causes a Huge Performance Gain both in terms of execution time and memory consumption.
  • Last but not least, it allows for a Declarative approach, as opposed to the traditional Imperative one. You simply Define your data, then you write your business logic (as usual in an imperative form), then Define the UI and it's relations to the data via DataBinding.

This basic concept applies to EVERYTHING in WPF.

With ListBoxes, ComboBoxes, Menus, TabControls, DataGrids, and ALL ItemsControls it's exactly the same:

You don't "populate a ListBox with UserControls", instead:

  • you create a proper ViewModel which contains an IEnumerable<T> which holds your data items. The ObservableCollection<T> is preferred because WPF listens to it's CollectionChanged event and updates the UI accordingly when items are added/removed from the collection:

    public class MyViewModel
    {
        public ObservableCollection<Person> People {get;set;}
    
        //Constructor
        public MyViewModel()
        {
           People = new ObservableCollection<Person>();
    
           //populate the collection here...
        }
    }
    
  • Then you Define the ListBox In XAML and use WPF's Data Templating capabilities to Define how the UI will look like for each item:

    <ListBox ItemsSource="{Binding People}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBox Text="{Binding LastName}"/>
                    <TextBox Text="{Binding FirstName}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox> 
    

    Note: It's the same if you want to use a UserControl in there instead:

            <DataTemplate>
                <my:MyUserControl/>
            </DataTemplate>
    
  • And finally set the UI's DataContext to an instance of the ViewModel:

    //Window Constructor
    public MainWindow()
    {
         InitializeComponent();
         DataContext = new MyViewModel();
    }
    

Note: I mentioned the INotifyPropertyChanged interface in the comments. this interface must be implemented by your Data Items or ViewModels in order to support Two-way Binding properly. Otherwise WPF has no way to "know" when a specific property (such as LastName) is changed and update the UI accordingly. I consider that topic to be out of the scope of this answer and therefore I won't get into it. You can read the linked material, and there is also plenty of material in the Web about it.

As I already mentioned, I have no experience at all in C++, therefore all these examples are in C#. Remember I also mentioned WPF only supports databinding to .Net Objects, and I don't think it supports lower-level in-memory structures such as the ones you may construct in C++.

My suggestion is that you use C# for the upper-level UI-facing part of your software, while leaving C++ for any lower-level operations you might need.

I strongly suggest reading thru the material linked in this post, most importantly Rachel's WPF Mentality and the ItemsControl-related material.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top