movra
Topic Author
Posts: 70
Joined: 02 Apr 2014, 20:35

TextureSource in ViewModel

27 Jul 2014, 17:39

I want to use the MVVM pattern as demonstrated in the Commands example. One twist: I want to add a list of images. So I'm going to use TextureSource.

Best practice (is it?) is to use only portable types in the ViewModel, ie. the Models and ViewModels can run independently of specific Views. TextureSource is more a View kind of thing, especially since you need to feed it a Unity3D Texture instance. Meaning instead of a collection of TextureSources you'd probably rather have a collection of strings in your ViewModel. In the View you would listen to collection change events, convert the strings to TextureSources and update the ItemsSource of the list controls.

Image

But let's see what happens if you use a collection of TextureSources in the ViewModel anyway.
[Noesis.Extended]
    public class MyViewModel : Noesis.SerializableComponent
    {
        private Collection _items;

        public Collection Items { get; set; }

        public MyViewModel()
        {       
           Init();
        };
      
        private void Init()
        {
            Items = new Collection();

            var cat = new ImageItem("cat");
            var bear = new ImageItem("bear");
            var dog = new ImageItem("dog");

            Items.Add(cat);
            Items.Add(bear);
            Items.Add(dog);
        }
[Noesis.Extended]
    public class ImageItem: Noesis.BaseComponent
    {
        private string _name;
        private Noesis.TextureSource _source;

        public ImageItem(string name)
        {
            UnityEngine.Debug.Log("Creating new ImageItem with Resource name " + name);

            _name = name;

            var texture2D = UnityEngine.Resources.Load(name) as UnityEngine.Texture2D;
            _source = new Noesis.TextureSource(texture2D);
        }

        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    NotifyPropertyChanged("Name");
                }
            }
        }

        public Noesis.TextureSource Img
        {
            get
            {
                return _source;
            }
            set
            {
                if (_source != value)
                {
                    _source = value;
                    NotifyPropertyChanged("Img");
                }
            }
        }
    }
<Grid>
 <Grid.Resources>
      <local:MyViewModel x:Key="ViewModel"/>
      <DataTemplate x:Key="ListItemTemplate">
         <Grid Width="200" Height="100">
            <Grid.ColumnDefinitions>
               <ColumnDefinition />
               <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="{Binding Name}"/>
            <Image Grid.Column="1" Source="{Binding Img}"/>
         </Grid>
      </DataTemplate>
   </Grid.Resources>

  <StackPanel Orientation="Vertical" DataContext="{StaticResource ViewModel}">
      <ListBox ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ListItemTemplate}" />
  </StackPanel>
</Grid>
When trying to save the XAML, the following exception is thrown.
NullReferenceException: Object reference not set to an instance of an object
Noesis.TextureSource.Noesis_CreateTextureSource (IntPtr texture, Int32 width, Int32 height, Int32 numMipMaps) (at Assets/Plugins/NoesisGUI/Scripts/Proxies/TextureSourceExtend.cs:146)
However it appears it's technically possible to build the XAML first without errors and having the list of images shown by taking the steps below:

1) comment out the Init() call in the constructor
2) build the XAML (eg. change a string and resave)
3) restore constructor

Now when you return to Unity3D, the error will not be thrown.

The question is: is this intended behavior, a bug or a missing feature? Or is it exactly as I said before, just plain wrong to use TextureSources in the ViewModel and the trick above is nothing more than an ugly hack?
 
User avatar
sfernandez
Site Admin
Posts: 2991
Joined: 22 Dec 2011, 19:20

Re: TextureSource in ViewModel

29 Jul 2014, 03:24

Hi,

As you have noticed, specifying the ViewModel as part of the xaml file introduces lots of limitations. First one, you have to make your ViewModel inherit from SerializableComponent. And worst, all the ViewModel public properties should be also serializable (basic types already are, but custom classes should inherit from SerializableComponent too).

The thing is that setting the ViewModel seems quite strange because you are tying it with the xaml. It would be better if you set the DataContext from the application logic. This way you avoid the serializable constraint, because ViewModel doesn't need to be serialized along with the UI tree defined by the xaml.

About the best way now to expose a list of TextureSource resources in a ViewModel, I will go for something like this:

viewtopic.php?f=3&t=431#p2084

But instead of storing strings in the dictionary, you can store ImageBrushes. Currently, with this solution you will be packing the textures along with the ResourceDictionary xaml, instead of using Unity textures, so you won't need to put the textures in a Resources folder.

Hope it helps.
 
User avatar
jsantos
Site Admin
Posts: 3917
Joined: 20 Jan 2012, 17:18
Contact:

Re: TextureSource in ViewModel

29 Jul 2014, 15:52

I think that the best alternative here is having the Unity.Texture2D in the ViewModel. And the ViewModel being a MonoBehaviour. That way, you can select the textures using the Unity Asset Browser, no need to copy anything to the /Resources folder.

We have plans to improve our NoesisGUI component in Unity by adding a new UI option to set a DataContext. But for now we need to improve our implementation to allow having VM that does not inherit from Noesis.BaseComponent.
 
movra
Topic Author
Posts: 70
Joined: 02 Apr 2014, 20:35

Re: TextureSource in ViewModel

29 Jul 2014, 18:11

Thank you for your replies!

For context, let's say I want to make a media browser. The application has a MediaList, implemented as a Noesis GUI scroll list, that can show images from online and offline databases. The images are wrapped in Image objects containing metadata such as name and a description. The user is able to request and filter media by tags.

The diagram would look something like this:

Image

And an example GUI:

Image
 
User avatar
jsantos
Site Admin
Posts: 3917
Joined: 20 Jan 2012, 17:18
Contact:

Re: TextureSource in ViewModel

29 Jul 2014, 18:19

Nice!

As soon as you can fill a Unity3D Texture instance (from local disk, from the web) the problem should be solved, shouldn't it?
 
movra
Topic Author
Posts: 70
Joined: 02 Apr 2014, 20:35

Re: TextureSource in ViewModel

29 Jul 2014, 18:27

Theoretically yes, but there's a philosophy to keep the ViewModel clean from Unity stuff. What if one day you want to run the application apart from Unity? So that's why I thought about using value converters or something and just use portable types only in the ViewModel. But... now I don't even know anymore what I'm asking for :lol: Let's just see what happens when we get uFrame and Noesis to play nice with each other.
 
movra
Topic Author
Posts: 70
Joined: 02 Apr 2014, 20:35

Re: TextureSource in ViewModel

30 Jul 2014, 14:32

In Microsoft's official PixPresenter sample project they store a byte array in the ViewModel and use a ValueConverter on the Image control to generate a BitmapImage object.

Similarly in the sample project Creating portable code in Windows Store Apps (PCL + MVVM + OData Services) she replaces the BitmapImage for an URI string, which is then bound to the Image control.

In both samples the BitmapImage has been replaced with something portable.

Who is online

Users browsing this forum: Semrush [Bot] and 15 guests