none
Is there any way to manage an IDisposable object from XAML?

    Soru

  • I have a XAML object, it is an ObservableCollection that is bound to a data model to keep the list reconciled with the server:

    class MyCollection : ObservableCollection<OrderType>
    {
        MyCollection()
        {
            DataModel.OrderType.OrderTypeRowChanged += new DataModel.OrderTypeRowChangedEventHandler(this.myhandler);
        }
    }

    How are we to use an object like this in XAML?  Its easy enough when you control the lifetime of the object, you can use the IDisposable interface and wrap the logic in a 'using' block or dispose of it explicitly with the 'Unloaded' event.  However, I've got ItemsControls that are bound to these lists that must be reconciled automatically with the data model underneath the controls.

    Any ideas?  What is the desired way to handle these kinds of objects


    Sincerely, Donald Roy Airey

    06 Mart 2012 Salı 14:44

Tüm Yanıtlar

  • Hi Donald,

        

    Start a new WPF project and paste this in:

      

    MainWindow.xaml

    <Window
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	x:Class="WpfApplication10.MainWindow"
    	x:Name="Window"
    	Title="MainWindow"
    	Width="640" Height="480"
        xmlns:local="clr-namespace:WpfApplication10">
    
    	<StackPanel x:Name="LayoutRoot">
    		<ListBox x:Name="list1" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" ItemTemplate="{DynamicResource DataTemplate1}" >
    			<ListBox.Resources>
    				<DataTemplate x:Key="DataTemplate1">
    					<local:MyButton Content="{Binding Name}"/>
    				</DataTemplate>
    			</ListBox.Resources>
    		</ListBox>
    		<Button Content="Test" Click="Button_Click" HorizontalAlignment="Left" Width="100" />
    	</StackPanel>
    </Window>

    MainWindow.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfApplication10
    {
        class MyClass : IDisposable
        {
            public string Name { get; set; }
    
            public void Dispose()
            {
                MessageBox.Show("Bye!");
            }
        }
    
        public class MyButton : Button
        {
            object myItem;
    
            public MyButton()
            {
                Unloaded += new RoutedEventHandler(MyButton_Unloaded);
                Loaded += new RoutedEventHandler(MyButton_Loaded);
            }
    
            void MyButton_Loaded(object sender, RoutedEventArgs e)
            {
                myItem = DataContext;
            }
    
            void MyButton_Unloaded(object sender, RoutedEventArgs e)
            {
                var dc = myItem as IDisposable;
                dc.Dispose();
            }
        }
        
        public partial class MainWindow : Window
    	{
    
                    public MainWindow()
    		{
    			this.InitializeComponent();
                            list1.ItemsSource = new List<MyClass> { new MyClass { Name = "one" }, new MyClass { Name = "two" } };
    		}
    
            private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
            {
                list1.ItemsSource = null;
            }
    
        }
    }
       

    If I understand you correctly, that is how you can tie it all together.

        

    Regards,
    Pete


    #PEJL

    06 Mart 2012 Salı 16:34
  • MVVM.

    The code that controls that sort of thing would be in the viewmodel code.

    06 Mart 2012 Salı 16:37
  • Pete,

        Thank you very much for the very detailed reply but it assumes that you override every ItemsControl that wants to use this list (or one like it).  In our product we have hundreds of buttons and comboboxes and dozens of ListViews.  All of them would like to use these lists like this that collect data from the data model.  The XAML generally looks like:

        <Grid.Resources>
            <data:MyCollection x:Key="myCollection"/>
        </Grid.Resources>
    
        <CollectionViewSource x:Key="myCollectionView"
                  Source="{StaticResource myCollection}">
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="SortOrder" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    
       So, you see, it isn't alway practical to override disposable behavior a control.  We would need to do this to every control that is to use data collected from the data model.  I think I know the answer to this one, just wondering if anyone's come up with a clever way to user IDisposable in an MVVM architecture.


    Sincerely, Donald Roy Airey

    06 Mart 2012 Salı 17:11
  • It's difficult to follow what your problem is.  If it's communicating between viewmodels and models then I would use mvvm light messaging.  No events to worry about adding and removing.
    06 Mart 2012 Salı 19:36
  • Andy,

    The issue is that my resources are not always related to a control directly.  Sometimes they are part of a view.  In the above example, I might have something like:

        <ListView ItemsSource="{StaticResource myCollectionView}"/>

    The solution above is a nice one, but it assumes that all IDisposable controls are used as the DataContext of the control and that we can re-engineer all the controls to handle the Unloaded event.

    I think the right approach -- and I'm still working on this -- is something similar to Pete's answer, but less generic.  I think I'm just going to have to bite the bullet and write the "OrderType" ComboBox so that it manages the list that's tied to the data model.  There doesn't seem to be any hope for a generic approach for using IDisposable objects from XAML.

    Remind me again why we got rid of Destructors?

    Thanks for your response.


    Sincerely, Donald Roy Airey

    06 Mart 2012 Salı 23:30
  • I still don't get it.

    You have a View that needs a collection of some data.

    The ViewModel is there to supply that data ( and handle commands, but let's set that aside).

    So why is your Itemssource not set to a collection which is a public property of the viewmodel?

    When your viewmodel goes out of scope, the collection goes out of scope, no pesky events to keep it in memory, job done.

    Instantiate the viewmodel as a private member of the view in it's constructor and your viewmodel automatically goes out of scope when your view goes out of scope.

    07 Mart 2012 Çarşamba 09:10
  • Andy,

    This is the issue: This particular view model must have event handlers installed in order to keep the view model reconciled to the data model.  When the view model goes out of scope, those event handlers need to be cleaned up (that is, the handlers in the view model need to be disengaged from the data model).  There is no mechanism to do this deterministically in C# (such as the explicit destructors in C++).  The IDisposable interface was build for explicitly cleaning up managed resources, but there is no method of calling IDisposable when the view model goes out of scope.

    The option suggested by Pete above uses the 'Unloaded' event of the control that 'owns' the view model.  This will work except that we've got a huge number of generic combo boxes, list boxes, and other ItemsControls that were all designed using just XAML.  This is a VERY powerful concept.  Now we will have to make specialized combo boxes (e.g. a specialized 'Order Type' combo box) that exposes the view model as a property.  These specialized controls can use their 'Unloaded' event hanedlers to call the IDisposable interface.

    It is a real pity there's nothing built into the architecture to see if the object is an IDisposable when it goes out of scope and immediately calls the interface (or some such similar device).


    Sincerely, Donald Roy Airey

    07 Mart 2012 Çarşamba 13:33
  • I thought I already solved your event handler problem.

    The way I would have such an application work:

    XAML in the View describes layout and binds to a property of Viewmodel which is set as the datacontext for the view.

    Viewmodel exposes list(s) of objects. Containing data and commands.  There would be one property which is a typed list per collection.  So you have 4 listboxes then you expose 4 collections from the viewmodel.

    Updates are notified between whichever lists and the a repository via messaging.

    The View instantiates the viewmodel.

    The view goes out of scope and the viewmodel goes out of scope with no events to mess things up.

    Everything disposes fine.

    No custom listboxes necessary.

    All my controls are designed using xaml.

    I still don't get where the problem is.

    In your code there's mycollection.

    That must come from somewhere.

    What's it bound to?

    Isn't that bound to a collection exposed from your viewmodel?

    07 Mart 2012 Çarşamba 17:27
  • Hi Donald Roy Airey,

    How about your concern?

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    20 Mart 2012 Salı 10:16
  • Sheldon,

    Why did you propose Andy's post as an answer?  It speaks in generalities without any specifics.  XAML_Guy's post was closer to a usable answer.  As far as I can tell, the real answer is that this is a flaw/feature of C#.  The IDisposable was created to deal with cases where you need an explicit destructor.  In the old days you would do the same thing by deleting the object explicitly.  XAML_Guy's pattern will work, but involves re-architecting many of the controls we're using.  I'm also looking at using the IWeakEvent interface to accomplish the same thing.

    Don

    20 Mart 2012 Salı 10:33
  • My answer was intended to describe enough for someone to understand the principle:

    Use messaging instead of events.

    Which part didn't you follow?

    20 Mart 2012 Salı 12:04
  • Andy,

    I followed it all, I just don't believe it's a proper implementation of MVVM.

    I had a specific question about the implementation MVVM and was looking for an concrete answer that I (and others with the same problem) could implement, not generalities.  The code left by XAML_Guy partially met my needs: the Loaded/Unloaded events seem like part of an answer, but it means I need to get rid of the shared data models created by statements like:

    <Grid.Resources>     <data:MyCollection x:Key="myCollection"/> </Grid.Resources> <CollectionViewSource :Key="myCollectionView" Source="{StaticResource myCollection}">     <CollectionViewSource.SortDescriptions>         <scm:SortDescription propertyName="SortOrder" />     </CollectionViewSource.SortDescriptions> </CollectionViewSource>

    <ListView ItemsSource="{StaticResource myCollectionView}"/>

    And bury the collections inside each control that wants to use them, using the Loaded/Unloaded to dispose of the event handlers required by 'myCollection'.

    In the example above, I would have to change the generic 'ListView' into an 'OrderTypeListView', and that's the rub.  We have dozens of these generic controls that work in many different scenarios with just a little templating.  Using a pure MVVM approach, I'm going to have to change each of the generic controls into into specific ones that incorporate the data model.  I suspected this was the direction I would have to take but I was hoping there was an easier way.  The IWeakEvent pattern works also, but I've decided that if you're in for a penny, you're in for a pound.

    I've bit the bullet and am rewriting all the generic controls to use a pure MVVM approach.

    • Düzenleyen DRAirey1 21 Mart 2012 Çarşamba 12:53 Misspelling Corrected
    20 Mart 2012 Salı 12:52
  • The messaging mechanisms allow transferring objects, which could therefore include typed collections.

    You could define at run time what each hooks up to via code and or DI.

    So messaging is extremely flexible and you don't need to worry about events retaining objects in memory.

    The only part of my suggested approach which is arguably not "pure" mvvm is that I recommend instantiating viewmodels from the view.

    I don't really see that as a downside but you could alternatively use property injection of the viewmodel to the view.  Useful if you wanted to use multiple viewmodels with a view.

    20 Mart 2012 Salı 16:01