locked
Binding listview to property of custom control RRS feed

  • Question

  • I have created a custom control called QueryFilterManager that manages a list of QueryFilter objects.  These QueryFilter objects are used to execute methods and return their results in an IList form. I need to bind the Results of a specified QueryFilter to a listview.  When I attempt this though, the listview is never populated.  The active filter from which I am attempting to get my results is a dependency property on the QueryFilterManager control.

    If I take the results of the active QueryFilter and assign it to a List that is a DependencyProperty in my OnLoaded event, the ListView will update propertly.  This however, is not an option because the active filter needs to be able to be changed at runtime.  Why is it that my binding does not work in this instance?  What can I do to make it work?

    My code is as follows.  Thanks in advance for your responses.


    Form Binding

    <z:QueryFilterManager Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" Name="QueryFilterManager"></z:QueryFilterManager>
                <z:SortableListView Grid.Row="1" Grid.Column="0" Height="Auto" Name="clientInterfacesListView" ItemsSource="{Binding QueryFilterManager.CurrentFilter.Results}" SelectionChanged="OnInterfaceSelectionChanged">
                    <ListView.View>
                        <GridView>
                            <GridViewColumn Header="Client" z:SortableListView.SortCriteria="Client.DisplayName, Name" DisplayMemberBinding="{Binding Path=Client.DisplayName}" Width="Auto"/>
                            <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="200"/>
                            <GridViewColumn Header="LastUpdateDate" DisplayMemberBinding="{Binding LastUpdateDate}"/>
                            <GridViewColumn Header="LastUpdateUser" DisplayMemberBinding="{Binding Path=LastUpdateUserID}"/>
                            <GridViewColumn Header="LastXMLUpdateDate" DisplayMemberBinding="{Binding LastXMLUpdateDate}"/>
                            <GridViewColumn Header="LastXMLUpdateUser" DisplayMemberBinding="{Binding Path=LastXMLUpdateUserID}"/>
                            <GridViewColumn Header="CreationDate" DisplayMemberBinding="{Binding CreationDate}"/>
                            <GridViewColumn Header="CreationUser" DisplayMemberBinding="{Binding Path=CreationUserID}"/>
                        </GridView>
                    </ListView.View>
                </z:SortableListView>




    QueryFilter Class

    public class QueryFilter
        {
            public string FetchAPI { get { return _fetchAPI; } set { _fetchAPI = value; } }
            protected string _fetchAPI;

            public string FetchMethod { get { return _fetchMethod; } set { _fetchMethod = value; } }
            protected string _fetchMethod;

            public string CriteriaToAppend { get { return _criteriaToAppend; } set { _criteriaToAppend = value; } }
            protected string _criteriaToAppend;

            public int ResultCount { get { return _resultCount; } set { _resultCount = value; } }
            protected int _resultCount;

            public IList Results { get { return _results; } set { _results = value; } }
            protected IList _results;

            public string FilterName { get { return _filterName; } set { _filterName = value; } }
            protected string _filterName;

            public bool CountOnly { get { return _countOnly; } set { _countOnly = value; } }
            protected bool _countOnly;

            public object[] Arguments { get { return _arguments; } set { _arguments = value; } }
            protected object[] _arguments;       
        }




    QueryFilterManager Class

    public partial class QueryFilterManager : UserControl
        {
            protected const string linkNameBase = "filterLink";

            public static readonly DependencyProperty QueryFiltersProperty = DependencyProperty.Register("QueryFilters", typeof(IList<QueryFilter>), typeof(QueryFilterManager));
            public IList<QueryFilter> QueryFilters { get { return GetValue(QueryFiltersProperty) as IList<QueryFilter>; } set { SetValue(QueryFiltersProperty, value); } }

            public static readonly DependencyProperty CurrentFilterProperty = DependencyProperty.Register("CurrentFilter", typeof(QueryFilter), typeof(QueryFilterManager));
            public QueryFilter CurrentFilter { get { return GetValue(CurrentFilterProperty) as QueryFilter; } set { SetValue(CurrentFilterProperty, value); } }

            public int FilterIndex { get { return _filterIndex; } set { _filterIndex = value; } }
            protected int _filterIndex;

            public virtual IApplicationContext ApplicationContext { get { return _applicationContext; } set { _applicationContext = value; } }
            protected IApplicationContext _applicationContext;

            public virtual ICoreAPI CoreAPI { get { return ApplicationContext["CoreAPI"] as ICoreAPI; } }

            public QueryFilterManager()
            {
                InitializeComponent();
            }
    }


    Wednesday, March 19, 2008 6:44 PM

Answers

  • I take it that the items in the list change dynamically, and no items are depicted other than those that were present when the list was first assigned to the listview.  This is default behaviour, no binding is broken.  This is because a simple list doesn't have any mechanism to let the listview know it's content was changed.  To fixt this, simply use ObservableCollection<T> instead.  This will automatically update the listview.
    Wednesday, March 19, 2008 8:24 PM
  • If you want UI can be refreshed when a collection changed, the collection should implement the INotifyCollectionChanged interface. The ObservableCollection class has already implemented interface. Furthermore, the ObservableCollection class also implemented IList interface. This is the WPF data binding mechanism. Otherwise, you must use CollectionView.Refresh method to update the UI manually. But this way has many disadvantages, for example, the current item index will be lost. So I think that your data source property should be changed to ObservableCollection.

     

    Best Regards,

    Wei Zhou

    Friday, March 21, 2008 3:34 AM

All replies

  • I take it that the items in the list change dynamically, and no items are depicted other than those that were present when the list was first assigned to the listview.  This is default behaviour, no binding is broken.  This is because a simple list doesn't have any mechanism to let the listview know it's content was changed.  To fixt this, simply use ObservableCollection<T> instead.  This will automatically update the listview.
    Wednesday, March 19, 2008 8:24 PM
  • The list is only updated by fetching an entirely new set for it, not through simply adding an item to it.  If a simple IList doesn't have the means to notify the listview that something is changed then how come it workes when the IList is a DependencyProperty on the form, but nothing is displayed when the IList is located on the QueryFilterManger control? 

    Current behavior is that an IList on my form works.  IList on my control doesn't.  I can assign the results of the IList on my control to the IList on my form and that will display, but this is too rigid of an approach for what I need.
    Wednesday, March 19, 2008 8:37 PM

  • to fixt this, simply use ObservableCollection<T> instead.  This will automatically update the listview.

    Also, I don't believe I would be able to use ObservableCollection<T> because the methods I am using to fetch my results are being called via reflection and the return types will vary with each use.  I believe that prevents me from using any paramterized types.  All I will know is that they will be coming back in some variety of IList.  It is also possible that the methods will simply be returning a count instead of an actual IList of results.  My code for running the methods is as follows.  


    Code Snippet

    //the method being called needs to have QueryFilter as a parameter    

    //entered by ref

    public virtual void ExecutePlan(ref QueryFilter filter)
            {
                if (filter.Arguments == null)
                    filter.Arguments = new object[0];
                object[] objectsToPass = new object[filter.Arguments.Length + 1];
                filter.Arguments.CopyTo(objectsToPass, 0);

                objectsToPass[filter.Arguments.Length] = filter;

                Type[] passTypes = new Type[objectsToPass.Length];
                for (int i = 0; i < objectsToPass.Length; i++)
                {
                    passTypes[i] = objectsToPass[i].GetType();
                }

                Type type = Type.GetType(filter.FetchAPI);
                MethodInfo method = type.GetMethod(filter.FetchMethod, passTypes);
                object instance;

                if (method.IsStatic)
                    instance = null;
                else
                {
                    Assembly asm = Assembly.GetAssembly(type);
                    instance = AppDomain.CurrentDomain.CreateInstanceFromAndUnwrap(asm.Location, type.FullName);
                }
                ((BaseAPI)instance).ApplicationContext = ApplicationContext;
                ((BaseAPI)instance).SetDAOs(this);
               
                method.Invoke(instance, objectsToPass);
            }




            
    Thursday, March 20, 2008 1:15 AM
  • If you want UI can be refreshed when a collection changed, the collection should implement the INotifyCollectionChanged interface. The ObservableCollection class has already implemented interface. Furthermore, the ObservableCollection class also implemented IList interface. This is the WPF data binding mechanism. Otherwise, you must use CollectionView.Refresh method to update the UI manually. But this way has many disadvantages, for example, the current item index will be lost. So I think that your data source property should be changed to ObservableCollection.

     

    Best Regards,

    Wei Zhou

    Friday, March 21, 2008 3:34 AM