How to keep UITypeEditor visible in PropertyGrid when object is not selected? - propertygrid

I'm writing a C# Windows form program and I'm having a hard time with PropertyGrids and UITypeEditor... I've read this post already: How to create custom PropertyGrid editor item which opens a form? which helped me getting started.
Here's the basic code I've got so far:
public FooClass Foo { get; private set; }
[Editor(typeof(FooEditor), typeof(UITypeEditor)),
TypeConverter(typeof(FooType))]
public class FooClass
{
private bool _enabled;
public bool Enabled
{
get { return _enabled; }
set { _enabled = value; }
}
public void ShowWindow()
{
using (var test = new Form())
{
test.ShowDialog();
}
}
}
private class FooType : ExpandableObjectConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
return "Click on the button to show the window";
}
}
private class FooEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
var svc = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
var foo = value as FooClass;
if (svc != null && foo != null)
{
foo.ShowWindow();
}
return null;
}
}
That works as expected: when the Foo object is selected in the propertygrid I see the "..." button on the right, I click and the window shows up:
What I can't figure out is how to make this "..." button stay visible even when the Foo object is not selected in the propertygrid:
Dis I miss something simple?

Related

Ignore member of base class in YamlDotNet

I have a class which I want to serialize with YamlDotNet:
public class AwesomeClass : PropertyChangedBase
{
private bool _element1;
private bool _enabled;
public bool Element1
{
get { return _element1; }
set
{
_element1 = value;
NotifyOfPropertyChange(() => Element1);
}
}
public bool Enabled
{
get { return _enabled; }
set
{
_enabled = value;
NotifyOfPropertyChange(() => Enabled);
}
}
}
My problem is, in the base class is an element named: IsNotifying
Is there a way to exclude this element from serialization, without the change of the base class?
You could override the property in the derived class and apply the YamlIgnore attribute there. While the sample below works, I suspect for more complicated class hierarchies you would really need to ensure no behavior changes.
public class AwesomeClass : PropertyChangedBase
{
[YamlIgnore]
public new bool IsNotifying
{
get { return base.IsNotifying; }
set { base.IsNotifying = value; }
}
[YamlIgnore]
public override bool Blah
{
get { return base.Blah; }
set { base.Blah = value; }
}
}
public class PropertyChangedBase
{
public bool IsNotifying
{
get;
set;
}
public virtual bool Blah
{
get;
set;
}
}
I had a similar problem (needed to filter properties of a particular type from classes I couldn't change, so using the attribute was not an option) and is what I came up with:
Create a custom type inspector:
public class MyTypeInspector : TypeInspectorSkeleton
{
private readonly ITypeInspector _innerTypeDescriptor;
public MyTypeInspector(ITypeInspector innerTypeDescriptor)
{
_innerTypeDescriptor = innerTypeDescriptor;
}
public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
{
var props = _innerTypeDescriptor.GetProperties(type, container);
props = props.Where(p => !(p.Type == typeof(Dictionary<string, object>) && p.Name == "extensions"));
props = props.Where(p => p.Name != "operation-id");
return props;
}
}
Create the serializer as follows:
var builder = new SerializerBuilder();
builder.WithTypeInspector(inspector => new MyTypeInspector(inspector));
var serializer = builder.Build();

Get BindingTarget in nested MvxDialogViewController in MvxCollectionViewCell

I have a ViewModel called LocationsViewModel, in which I have a ObservableCollection<LocationViewModel>. Additionally I have a LocationsView, which is an MvxCollectionViewController, in which I create a binding set and bind a MvxCollectionViewSource to the ObservableCollection.
In the LocationCell, which is a MvxCollectionViewCell, I want to display a MonoTouch.Dialog which is bound to various properties in the currently
selected LocationViewModel. The easiest way seems to be to create a nested MvxDialogViewController in the MvxCollectionViewCell, however to bind the
Elements in the MvxDialogViewController, I obviously need to create a Binding Target. My question is really can I pass a binding target from the MvxCollectionViewCell to the MvxDialogViewController?
Let me also try to explain it briefly with some code to improve the understanding.
LocationsViewModel.cs
public class LocationsViewModel : MvxViewModel
{
...
public ObservableCollection<LocationViewModel> Locations
{
get { return _locationDataService.Locations.Locations; }
}
...
}
LocationViewModel.cs
public class LocationViewModel : MvxNotifyPropertyChanged
{
...
//Tons of public properties like:
public string Name
{
get { return LinkedDataModel.Name; }
set
{
LinkedDataModel.Name = value;
RaisePropertyChanged(() => Name);
}
}
public double CurrentNoiseLevel
{
get { return LinkedDataModel.CurrentNoiseLevel; }
set
{
LinkedDataModel.CurrentNoiseLevel = value;
RaisePropertyChanged(() => CurrentNoiseLevel);
}
}
...
}
LocationsView.cs
[Register("LocationView")]
public class LocationsView
: MvxCollectionViewController
{
static readonly NSString LocationCellId = new NSString("LocationCell");
private readonly bool _isInitialized;
public LocationsView()
: base(new UICollectionViewFlowLayout()
{
MinimumInteritemSpacing = 0f,
ScrollDirection = UICollectionViewScrollDirection.Horizontal,
MinimumLineSpacing = 0f
})
{
_isInitialized = true;
ViewDidLoad();
}
public new LocationsViewModel ViewModel
{
get { return (LocationsViewModel)base.ViewModel; }
set { base.ViewModel = value; }
}
public sealed override void ViewDidLoad()
{
if (!_isInitialized)
return;
base.ViewDidLoad();
CollectionView.RegisterClassForCell(typeof(LocationCell), LocationCellId);
var source = new MvxCollectionViewSource(CollectionView, LocationCellId);
CollectionView.Source = source;
CollectionView.PagingEnabled = true;
var set = this.CreateBindingSet<LocationsView, LocationsViewModel>();
set.Bind(source).To(vm => vm.Locations);
set.Apply();
CollectionView.ReloadData();
}
}
LocationCell.cs
public class LocationCell : MvxCollectionViewCell
{
[Export("initWithFrame:")]
public LocationCell(RectangleF frame)
: base(string.Empty, frame)
{
InitView();
}
public LocationCell(IntPtr handle)
: base(string.Empty, handle)
{
InitView();
}
private void InitView()
{
var cell = new LocationCellDialog();
ContentView.Add(cell.View);
}
public class LocationCellDialog
: MvxDialogViewController
{
public LocationCellDialog()
: base(UITableViewStyle.Grouped, null, true)
{ }
public override void ViewDidLoad()
{
base.ViewDidLoad();
//How do I get the target here?
var target = ??;
Root = new RootElement
{
new Section
{
new StringElement().Bind(target, t => t.Name),
new StringElement().Bind(target, t => t.CurrentNoiseLevel)
}.Bind(target, t => t.Name),
};
}
}
}
So the question is can I simply pass along a binding target from the parent LocationCell to nested LocationCellDialog or is that a no go?
Each bindable view in MvvmCross has its own DataContext
For a top level View this DataContext is the ViewModel
For a Cell within a List, Table or Collection then the DataContext is set to the object in the list which the Cell is currently showing.
If you want to data-bind any property within a Cell to a property path on the DataContext then you can do so using the Fluent binding syntax.
For example, to bind the Text value of a child UILabel called myLabel to a child property Name on a Person in the list you could use:
this.CreateBinding(myLabel).For(label => label.Text).To<Person>(p => p.Name).Apply();
Or if you wanted to bind the Text to the Person itself you could use:
this.CreateBinding(myLabel).For(label => label.Text).Apply();
In your LocationCell I think you are saying you want to bind the DataContext of the nested LocationCellDialog to the DataContext of the containing cell.
To do this you should be able to use:
private void InitView()
{
var cell = new LocationCellDialog();
ContentView.Add(cell.View);
this.CreateBinding(cell).For(cell => cell.DataContext).Apply();
}

How to reflect changes in viewmodel to tableviewcell view with binding in MVVMcross

Am a little stuck with getting changes reflected from the ViewModel to the View when used in a MvxBindableTableViewCell. I am using the vNext branch of MvvmCross on iOS.
Everything is set up properly and the initial values are visible when loading/showing the list for the first time. The list is a ObservableCollection<T> and the ViewModels inherit from MvxViewModel (thus implements INotifyPropertyChanged).
The main ViewModel looks like this:
public abstract class BaseViewModel : MvxViewModel, IMvxServiceConsumer
{
//... just regular implementation
}
public class UploadListViewModel: BaseViewModel
{
private readonly IUploadItemTasks uploadItemTasks;
private readonly IPhotoPickerService photoPickerService;
public IObservableCollection<UploadItemViewModel> Uploads { get { return this.LoadUploadItems(); } }
public UploadListViewModel()
{
this.uploadItemTasks = this.GetService<IUploadItemTasks>();
this.photoPickerService = this.GetService<IPhotoPickerService>();
}
private IObservableCollection<UploadItemViewModel> LoadUploadItems()
{
using (var unitOfWork = UnitOfWork.Start ())
{
return new SimpleObservableCollection<UploadItemViewModel>(uploadItemTasks.GetAll());
}
}
public void StartUpload ()
{
if (this.Uploads == null || this.Uploads.Count == 0) {
ReportError("Error", "No images to upload");
return;
}
this.Uploads.ForEach (uploadItem => PostCallback (uploadItem));
}
private void PostCallback (UploadItemViewModel uploadAsset)
{
IProgressReporter progressReporter = uploadAsset;
this.photoPickerService.GetAssetFullImage(uploadAsset.ImageUrl,
(image) => {
UIImage fullImage = image;
NSData jpeg = fullImage.AsJPEG();
byte[] jpegBytes = new byte[jpeg.Length];
System.Runtime.InteropServices.Marshal.Copy(jpeg.Bytes, jpegBytes, 0, Convert.ToInt32(jpeg.Length));
MemoryStream stream = new MemoryStream(jpegBytes);
Uri destinationUrl = new Uri(uploadAsset.DestinationUrl + "&name=" + uploadAsset.Name + "&contentType=image%2FJPEG");
//TO DO: Move this to plugin
var uploader = new Uploader().UploadPicture (destinationUrl, stream, UploadComplete, progressReporter);
uploader.Host = uploadAsset.Host;
ThreadPool.QueueUserWorkItem (delegate {
uploader.Upload ();
jpeg = null;
});
});
}
private void UploadComplete (string name)
{
if (name == null){
ReportError("Error","There was an error uploading the media.");
} else
{
//ReportError("Succes", name);
}
}
The item ViewModel looks like:
public interface IProgressReporter
{
float Progress { get; set;}
}
public abstract class BaseAssetViewModel: BaseViewModel, IBaseAssetViewModel
{
//... just regular properties
}
public class UploadItemViewModel: BaseAssetViewModel, IProgressReporter
{
public UploadItemViewModel(): base()
{
}
private float progress;
public float Progress {
get {
return this.progress;
}
set {
this.progress = value;
this.RaisePropertyChanged(() => Progress);
}
}
}
The View for the items inherits from MvxBindableTableViewCell and has the property:
private float progress;
public float ProgressMarker {
get {
return progress;
}
set {
progress = value;
// change progressbar or textfield here
}
}
The tableviewcell is bounded to the UploadItemViewModel via the BindingText:
public const string BindingText = #"ProgressMarker Progress, Converter=Float;";
The Uploader class mentioned in the snippet of UploadListViewModel implements a private method which tries to set the progress on the IProgressReporter.
float progressValue;
void SetProgress (float newvalue)
{
progressValue = newvalue;
this.dispatcher.InvokeOnMainThread (delegate {
if (ProgressReporter != null)
ProgressReporter.Progress = progressValue;
});
}
During the first viewing of the list I can see that the properties in both the ViewModel and View are being hit but when I update the ViewModel via the interface IProgressReporter with a new value in Progress the View in the tableviewcell is not updated nor the property is being called.
What am I doing wrong or what am I missing here?
UPDATE: Check the answer to this question.
I found why the binding didn't work. I was replacing the ObservableCollection over and over again.. I changed that piece of code as stated below and now it reflects the changes made to the UploadItemViewModel in the View of the cell.
private IObservableCollection<UploadItemViewModel> uploads;
private IObservableCollection<UploadItemViewModel> LoadUploadItems()
{
if (uploads == null)
{
using (var unitOfWork = UnitOfWork.Start ())
{
uploads = new SimpleObservableCollection<UploadItemViewModel>(uploadItemTasks.FindAll());
}
}
return uploads;
}

Custom bindable control in a MvvmCross Touch project

I have a MvxBaseBindableCollectionViewCell which loads a xib that contains a custom button. I would like to be able to pass this custom button a ViewModel to bind to. Is this possible?
I'm trying to acheive something like MyButton.ViewModel = ViewModel.ChildViewModel and have ViewModel.ChildViewModel.Name show as the button title.
If you want to custom bind a cell, then there's a tutorial on this in http://slodge.blogspot.co.uk/2013/01/uitableviewcell-using-xib-editor.html
If you want to create a fully bindable UIButton within that View then you can do this using some inheritance like:
[Register("MyButton")]
public class MyButton
: UIButton
, IMvxServiceConsumer
{
private IList<IMvxUpdateableBinding> _bindings;
private const string BindingText = "SpecialTitle Customer.Name";
public MyButton()
{
}
public MyButton(IntPtr handle)
: base(handle)
{
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (var binding in _bindings)
{
binding.Dispose();
}
_bindings.Clear();
}
base.Dispose(disposing);
}
private object _dc;
public object DataContext
{
get { return _dc; }
set
{
_dc = value;
if (_bindings == null)
{
var binder = this.GetService<IMvxBinder>();
_bindings = binder.Bind(_dc, this, BindingText).ToList();
}
else
{
foreach (var binding in _bindings)
{
binding.DataContext = _dc;
}
}
}
}
public string SpecialTitle
{
get { return this.GetTitle(UIControlState.Normal); }
set { this.SetTitle(value, UIControlState.Normal); }
}
}
Aside> MvvmCross v3 "Hot Tuna" will contain some helper classes to make this a bit simpler to do.

How to inherit partial class for stored procedure call

I have this class as parent class:
public partial class GetStuffResult
{
private int _Id;
private string _Name;
public GetStuffResult()
{
}
[Column(Storage="_Id", DbType="INT NOT NULL")]
public int Id
{
get
{
return this._Id;
}
set
{
if ((this._Id != value))
{
this._Id = value;
}
}
}
[Column(Storage="_Name", DbType="NVarChar(100)")]
public string Name
{
get
{
return this._Name;
}
set
{
if ((this._Name != value))
{
this._Name = value;
}
}
}
}
This is base class which has same methods with exception of an extra method:
public partial class GetStuffResult1
{
private int _Score;
private int _Id;
private string _Name;
public GetStuffResult1()
{
}
[Column(Storage="_Score", DbType="INT NOT NULL")]
public int Id
{
get
{
return this._Score;
}
set
{
if ((this._Score != value))
{
this._Score = value;
}
}
}
[Column(Storage="_Id", DbType="INT NOT NULL")]
public int Id
{
get
{
return this._Id;
}
set
{
if ((this._Id != value))
{
this._Id = value;
}
}
}
[Column(Storage="_Name", DbType="NVarChar(100)")]
public string Name
{
get
{
return this._Name;
}
set
{
if ((this._Name != value))
{
this._Name = value;
}
}
}
}
I have done inheritance before but i am totally confused how it will work in this scenario? How can i inherit GetStuffResult so that i can use its 2 methods and dont have to copy paste same code twice in GetStuffResult1.
Will appreciate if someone can give example with code as i am new to .net 3.5 and still trying to learn it.
I am not sure if I correctly understood your question. (Your current code for GetStuffResult1 shouldn't compile as you have define Id property twice.) If you are looking to inherit from GetStuffResult then this would do (See Inheritance):
public partial class GetStuffResult1 : GetStuffResult
{
private int _Score;
public GetStuffResult1()
{
}
[Column(Storage = "_Score", DbType = "INT NOT NULL")]
public int Id
{
get
{
return this._Score;
}
set
{
if ((this._Score != value))
{
this._Score = value;
}
}
}
}
Notice that I have removed _Id and _Name from the child class. This however will give you warning that:
GetStuffResult1.Id' hides inherited member
'ThreadConsoleApp.GetStuffResult.Id'. Use the new keyword if hiding
was intended.
The second thing I am thinking about your question if you are confused about using partial classes and you may need a single class in multiple source file. In that case you may use partial keyword. If that is the case and you don't need inheritance then you need to use a single name for the class. e.g. GetStuffResult. In that particular case your GetStuffResult1 will become:
public partial class GetStuffResult
{
private int _Score;
public GetStuffResult1()
{
}
[Column(Storage = "_Score", DbType = "INT NOT NULL")]
public int Id
{
get
{
return this._Score;
}
set
{
if ((this._Score != value))
{
this._Score = value;
}
}
}
}
This will be similar to having a single class with all the combined properties.
Edit:
To access the base class properties in the child class, you may use base keyword.
base.Id = 0;
base.Name = "SomeName";
To access the base class properties from the object of GetStuffResult1, see the following example.
GetStuffResult1 gsr1 = new GetStuffResult1();
gsr1.Id = 0;
gsr1.Name = "SomeName";
Here gsr1.Name is from the base class, you may use different name for Id in either base or child class so that it can be more clearer.

Resources