Re: [Unity] Binding to an object other than BaseComponent
In uFrame Element designer we create a TestChat element, add the Input and Output properties and the SayHelloCommand. We also connect a TestChatView to the element. This will create TestChatViewModel.cs, TestChatController.cs and TestChatView.cs.I have no idea what you are talking about here, can you elaborate?In the meantime I know I can write Views to bind ViewModel properties to XAML controls, but it's redundant and error-prone. Would be a much more elegant solution if you could just plug in the ViewModel.

Now we can create the following XAML (derived from the sample Command.xaml). This is what I meant by plugging in the ViewModel.
Code: Select all
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
xmlns:local="clr-namespace:TestChat">
<Grid.Resources>
<local:TestChatViewModel x:Key="TestChatViewModel"/>
</Grid.Resources>
<Viewbox>
<StackPanel DataContext="{StaticResource TestChatViewModel}">
<TextBox Text="{Binding Input, Mode=TwoWay}"/>
<Button Content="Say Hello" Command="{Binding SayHelloCommand}" />
<TextBlock Text="{Binding Output}" />
</StackPanel>
</Viewbox>
</Grid>Since that is currently not the case, we have to bind the controls to the ViewModel properties ourselves. We also need to rip-out the bindings from the XAML and add Names to the controls instead so we can find them. The TestChatView.cs would look like this:
Code: Select all
using UnityEngine;
using Noesis;
public partial class TestChatView
{
private NoesisGUIPanel _noesisGUIPanel;
private Grid _grid;
private TextBox _inputTextbox;
private Button _SayHelloButton;
private TextBlock _OutputTextblock;
public override void Awake()
{
base.Awake();
_noesisGUIPanel = GetComponent<NoesisGUIPanel>();
_grid = _noesisGUIPanel.GetRoot<Grid>();
_inputTextbox = _grid.FindName<TextBox>("textbox_Input");
_SayHelloButton = _grid.FindName<Button>("button_SayHelloCommand");
_OutputTextblock = _grid.FindName<TextBlock>("textblock_Output");
_SayHelloButton.Click += _SayHelloButton_Click;
}
private void _SayHelloButton_Click(BaseComponent sender, RoutedEventArgs e)
{
ExecuteSayHelloCommand();
}
public override void InputChanged(string value)
{
base.InputChanged(value);
_inputTextbox.SetText(value);
}
public override void OutputChanged(string value)
{
base.OutputChanged(value);
if (string.IsNullOrEmpty(value))
{
_OutputTextblock.SetText(string.Empty);
}
else
{
_OutputTextblock.SetText(value);
}
}
public void Update()
{
TestChat.Input = _inputTextbox.GetText();
}
}Code: Select all
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<Viewbox>
<StackPanel>
<TextBox Name="textbox_Input"/>
<Button Content="Say Hello" Name="button_SayHelloCommand" />
<TextBlock Name="textblock_Output" />
</StackPanel>
</Viewbox>
</Grid>Finally you have to add TestChatView.cs to the Unity scene which would otherwise not be necessary.


So I have talked about this with uFrame's developer who told me the main problem is that Mono does not contain System.Windows.Input.ICommand nor System.Collections.Specialized.ObservableCollection. Remember that Noesis GUI also doesn't use System.Windows.Input.ICommand, but Noesis.BaseCommand instead. I wonder if there's a solution that solves this kind of solution for both Noesis and uFrame.
Here's a similar case about Implementing MVVM in WPF without using System.Windows.Input.ICommand.
Re: [Unity] Binding to an object other than BaseComponent
We are in contact with the uFrame developer about this. I expect to have a talk with him in Skype very soon. There are several things that must be solved in our side to improve the quality of the code generated although in the current state I think that this can be solved this way.
We think that the XAML must not be modified. The solution is using a wrapper around the ViewModel provided by uFrame. Although this wrapper can be written manually right now we think that it could be automatically generated by uFrame. The idea is the following:
And instead of using the VM provided by uFrame, use this one instead as the DataContext of the Xaml.
A wrap for our BaseCommand is also needed. This wrap would be used by the class TestChatViewModelNoesis posted above. The idea would be:
What do you think about this approach?
We think that the XAML must not be modified. The solution is using a wrapper around the ViewModel provided by uFrame. Although this wrapper can be written manually right now we think that it could be automatically generated by uFrame. The idea is the following:
Code: Select all
[Noesis.Extended]
// This could be generated automatically by uFrame
public class TestChatViewModelNoesis : Noesis.BaseComponent
{
private TestChatViewModel _viewModel;
public TestChatViewModelNoesis(TestChatViewModel viewModel)
{
_viewModel = viewModel;
}
// ... wrap of ViewModel properties
}
Code: Select all
public partial class TestChatView
{
public override void Awake()
{
base.Awake();
var gui = GetComponent<NoesisGUIPanel>();
var root = _noesisGUIPanel.GetRoot<Grid>();
root.SetDataContext(new TestChatViewModelNoesis(this.uFrameTestChatViewModel);
}
// ...
}
Code: Select all
[Noesis.Extended]
public class UFrameCommand: public Noesis.BaseCommand
{
private UFrame::ICommand _command;
public UFrameCommand(object sender, UFrame::ICommand command)
{
_command = command;
_command.Sender = sender;
}
public bool CanExecuteCommand(Noesis.BaseComponent parameter)
{
return true;
}
public void ExecuteCommand(Noesis.BaseComponent parameter)
{
_command.Parameter = parameter;
_command.Execute();
}
// ...
}
Re: [Unity] Binding to an object other than BaseComponent
Thanks for looking into this subject thoroughly and discussing it with the uFrame developers!
You know, I started writing a similar wrapper, copying the properties by hand. But I got stuck associating the uFrame ViewModel to wrapper. You solved it by setting the DataContext to a new instance of the wrapper initialized with the existing ViewModel.
Also I see that if you set the DataContext in the XAML, it will look for a parameter-less constructor. It's quite ingenious then to set it programmatically in the uFrame View, especially since you still occasionally need a View to do things. For example disabling a button when textboxes in a form are empty, which in XAML would require MultiDataTrigger.
A nice side-effect is that you can now edit the XAML properly in Kaxaml. Sure you could use Blend after adding all the assemblies, but it's incredibly slow and bloated.
However I hit a few problems:
You know, I started writing a similar wrapper, copying the properties by hand. But I got stuck associating the uFrame ViewModel to wrapper. You solved it by setting the DataContext to a new instance of the wrapper initialized with the existing ViewModel.
Also I see that if you set the DataContext in the XAML, it will look for a parameter-less constructor. It's quite ingenious then to set it programmatically in the uFrame View, especially since you still occasionally need a View to do things. For example disabling a button when textboxes in a form are empty, which in XAML would require MultiDataTrigger.
A nice side-effect is that you can now edit the XAML properly in Kaxaml. Sure you could use Blend after adding all the assemblies, but it's incredibly slow and bloated.
However I hit a few problems:
- Collections: uFrame uses System.Collections.Generic.ICollection<T> and Noesis has its own Collection class which seems to be some sort of replacement for System.Collections.ObjectModel.ObservableCollection<T>. The different types of collection have to be converted somewhere.
I wonder... Right now uFrame and Noesis both have their own different variations of Microsoft's MVVM classes that are not available in Mono. Wouldn't it a more elegant solution to port those classes to Mono? Instead of requiring specific code generators, wrappers and converters, Noesis and uFrame would be able to interact with each other and any other MVVM software out-of-the-box. But I have no idea how it would work in practice without breaking existing code.
Maybe there is something useful in here: MVVMCross or here Mosa. What about this, faking ObservableCollection? - The data flow in the direction from Noesis to uFrame works. The uFrame View properties are updated with the state of the Noesis GUI. Unfortunately the other way around doesn't seem to work for me at the moment.
In the View (CustomerView.cs):
In the wrapper (CustomerViewModelNoesis.cs):Code: Select allpublic override void FirstNameChanged(string value) { UpdateCanSave(); } public override void LastNameChanged(string value) { UpdateCanSave(); } private void UpdateCanSave() { bool isValidForm = !(Customer.LastName.IsNullOrWhiteSpace() || Customer.FirstName.IsNullOrWhiteSpace()); Customer.CanSave = isValidForm; Debug.Log("Can Save: " + isValidForm); } public override void CanSaveChanged(bool value) { Debug.Log("CanSaveChanged to: " + value); }
In the XAML (CustomerEditor.xaml)Code: Select allpublic bool CanSave { get { return _customerViewModel.CanSave; } set { if (_customerViewModel.CanSave != value) { _customerViewModel.CanSave = value; NotifyPropertyChanged("CanSave"); } } }
The debug.log messages will be shown in the console but the button won't change state.Code: Select all<Button Content="Save" IsEnabled="{Binding CanSave}" Command="{Binding SaveCommand}" />
Re: [Unity] Binding to an object other than BaseComponent
Re: 2nd problem: I'm an idiot. When writing to the ViewModel, you should write to the wrapper, not directly to the ViewModel.
This makes me think though. What if the ViewModel is written to from a different source, eg. a remote service? Seems like the wrapper needs bindings to the ViewModel too.
Code: Select all
private CustomerViewModelNoesis _wrapper;
public override void Awake()
{
base.Awake();
_panel = GetComponent<NoesisGUIPanel>();
_grid = _panel.GetRoot<Grid>();
_wrapper = new CustomerViewModelNoesis(Customer);
_grid.SetDataContext(_wrapper);
}
private void UpdateCanSave()
{
bool isValidForm = !(Customer.LastName.IsNullOrWhiteSpace() || Customer.FirstName.IsNullOrWhiteSpace());
_wrapper.CanSave = isValidForm;
Debug.Log("Can Save: " + isValidForm);
}
Re: [Unity] Binding to an object other than BaseComponent
Yes, this is a problem right now, we need to solve it. For now, the fast hack is copying the elements from uFrame container to our ObservableCollection.
- Collections: uFrame uses System.Collections.Generic.ICollection<T> and Noesis has its own Collection class which seems to be some sort of replacement for System.Collections.ObjectModel.ObservableCollection<T>. The different types of collection have to be converted somewhere.
The proper solution is that noesisGUI understands the IList<T> interface, as uFrame is doing. That way, both containers would be compatible.
Our idea is porting those classes to Mono and putting them under the Noesis namespace. uFrame developer will probably will want them under another namespace. The same for another thirdparty developers. I think that at the end bridge classes are needed. What you are suggesting is the ideal, if we found compromise with the uFrame developer the wrapper class won't be needed. Anyway I think that the automatic generation of the wrapper from the uFrame framework is a good compromise.I wonder... Right now uFrame and Noesis both have their own different variations of Microsoft's MVVM classes that are not available in Mono. Wouldn't it a more elegant solution to port those classes to Mono? Instead of requiring specific code generators, wrappers and converters, Noesis and uFrame would be able to interact with each other and any other MVVM software out-of-the-box. But I have no idea how it would work in practice without breaking existing code.
Maybe there is something useful in here: MVVMCross or here Mosa. What about this, faking ObservableCollection?
Yes, a mechanism is needed to communicate changes from the inner properties to the wrapper. One more thing to the list.This makes me think though. What if the ViewModel is written to from a different source, eg. a remote service? Seems like the wrapper needs bindings to the ViewModel too.
Re: [Unity] Binding to an object other than BaseComponent
Some randomly assorted thoughts.
In .NET 4 Microsoft moved the important MVVM classes to System.dll, except for ICommand which is not moved to System.dll until .Net 4.5. Mono already contains ports of those classes: https://github.com/mono/mono/tree/maste ... ass/System
Unity5 will feature IL2CPP, enabling Unity to upgrade to a newer version of Mono C#. Until then maybe you can include the Mono ports and subclass them?
There are some gotchas with WPF. For example, you mentioned copying elements to Noesis.Collection. You have to do that one item at a time. Every time you add one item Noesis does a P/Invoke and notifies the listeners.In some cases it would be better to be able to add a batch of items at once. Imagine updating a ListBox with 1000 items. The solutions to this problem and similar performance problems are found here: http://pelebyte.net/blog/2011/07/11/twe ... rformance/
In .NET 4 Microsoft moved the important MVVM classes to System.dll, except for ICommand which is not moved to System.dll until .Net 4.5. Mono already contains ports of those classes: https://github.com/mono/mono/tree/maste ... ass/System
Unity5 will feature IL2CPP, enabling Unity to upgrade to a newer version of Mono C#. Until then maybe you can include the Mono ports and subclass them?
There are some gotchas with WPF. For example, you mentioned copying elements to Noesis.Collection. You have to do that one item at a time. Every time you add one item Noesis does a P/Invoke and notifies the listeners.In some cases it would be better to be able to add a batch of items at once. Imagine updating a ListBox with 1000 items. The solutions to this problem and similar performance problems are found here: http://pelebyte.net/blog/2011/07/11/twe ... rformance/
Re: [Unity] Binding to an object other than BaseComponent
Yes, when talking with the uFrame developer we agreed that the best would creating a github repository with these interfaces. Both noesisGUI and uFrame will include that project in a folder named MVVM of the Unity Project.Some randomly assorted thoughts.
In .NET 4 Microsoft moved the important MVVM classes to System.dll, except for ICommand which is not moved to System.dll until .Net 4.5. Mono already contains ports of those classes: https://github.com/mono/mono/tree/maste ... ass/System
Unity5 will feature IL2CPP, enabling Unity to upgrade to a newer version of Mono C#. Until then maybe you can include the Mono ports and subclass them?
The idea of copying elements mentioned in this thread should be considered a hack to have something working and experimental to play with but it is not the final solution. What we want to achieve is supporting the IList interface. As uFrame is going to do the same, collections will be able to be shared between both packages.There are some gotchas with WPF. For example, you mentioned copying elements to Noesis.Collection. You have to do that one item at a time. Every time you add one item Noesis does a P/Invoke and notifies the listeners.In some cases it would be better to be able to add a batch of items at once. Imagine updating a ListBox with 1000 items. The solutions to this problem and similar performance problems are found here: http://pelebyte.net/blog/2011/07/11/twe ... rformance/
Thanks a lot for all your investigation. It is helping us a lot!
Who is online
Users browsing this forum: Google [Bot] and 7 guests