I'm currently having a issue with the SeekBar class in MonoDroid.
Currently I have extended it like this:
public class TTSeekBar : SeekBar, ITTComponent
{
public TTSeekBar(Context context): base(context)
{
}
private int _min = 0;
public int Min { get { return _min; } set { _min = value;} }
public override int Progress
{
get
{
return base.Progress + _min;
}
set
{
base.Progress = value;
}
}
public override int Max
{
get
{
return base.Max + _min;
}
set
{
base.Max = value + _min;
}
}
public object GetInputData()
{
return (this.Progress + _min).ToString();
}
}
But whenever I try to create a object using TTSeekBar _seekBar = new TTSeekBar(this); (where this is a Activity) I get a Sytem.NotSupportedException thrown at the constructor with the message
Unable to activate instance of type TTApp.TTSeekBar from native
handle 44fdad20
Extending other components of the Android.Widget namespace like this seems to work just fine, so I'm wondering why this one doens't work.
Just tested this on API Level 8 and it seems to work.
using System;
using System.Globalization;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Util;
using Android.Widget;
using Android.OS;
namespace AndroidApplication1
{
[Activity(Label = "AndroidApplication1", MainLauncher = true, Icon = "#drawable/icon")]
public class Activity1 : Activity
{
int count = 1;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button>(Resource.Id.MyButton);
button.Click += delegate { button.Text = string.Format("{0} clicks!", count++); };
var seekbar = new TTSeekBar(this);
var ll = FindViewById<LinearLayout>(Resource.Id.LinearLayout);
ll.AddView(seekbar);
}
}
public class TTSeekBar : SeekBar
{
protected TTSeekBar(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public TTSeekBar(Context context) : base(context)
{
}
public TTSeekBar(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public TTSeekBar(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
private int _min = 0;
public int Min { get { return _min; } set { _min = value; } }
public override int Progress
{
get
{
return base.Progress + _min;
}
set
{
base.Progress = value;
}
}
public override int Max
{
get
{
return base.Max + _min;
}
set
{
base.Max = value + _min;
}
}
public object GetInputData()
{
return (Progress + _min).ToString(CultureInfo.InvariantCulture);
}
}
}
So as I said you just need to implement the right constructors and it should work just fine.
There is an explanation as to why here: MonoDroid: Error when calling constructor of custom view - TwoDScrollView
Related
I am using WinUI 3 UWP TabView in my App. I know that WinUI 3 is still in Preview stage for UWP. But still I want to know a workaround for my issue as I want to use TabView in my App. I have gone through the Official Documentation and GitHub Samples but I couldn't find a solution. The TabView is NOT displaying a New Tab whenever a New Document is added to the Collection. I have searched a lot but couldn't find a solution. Kindly, share a solution/workaround. You might suggest using WinUI 2 since it is stable for UWP. But, I have already tried that and WinUI 2 controls are not blending well with existing UWP Controls. But WinUI 3 blends perfectly. All other controls except TabView are working well. When I switch from DataBinding to Manually maintaining a list of TabItems, it works perfectly. But, I don't want Boilerplate code. I want to achieve the same with DataBinding. I am new to MVVM. So, if there's a problem with my ViewModel, do share a workaround.
This is my ViewModel Class:
using Microsoft.UI.Xaml.Controls;
using System.ComponentModel;
using System.IO;
using System.Text;
using MyApp.Utilities;
using System.Runtime.CompilerServices;
namespace MyApp.ViewModels
{
public class TextDocument : INotifyPropertyChanged
{
private int _documentId;
private string _fileName;
private string _filePath;
private string _textContent;
private Encoding _encoding;
private LineEnding _lineEnding;
private bool _isSaved;
public int DocumentId
{
get
{
return _documentId;
}
set
{
_documentId = value;
OnPropertyChanged(ref _documentId, value);
}
}
public string FileName
{
get
{
return _fileName;
}
set
{
OnPropertyChanged(ref _fileName, value);
}
}
public string FilePath
{
get
{
return _filePath;
}
set
{
OnPropertyChanged(ref _filePath, value);
FileName = Path.GetFileName(_filePath);
}
}
public string TextContent
{
get
{
return _textContent;
}
set
{
OnPropertyChanged(ref _textContent, value);
}
}
public Encoding FileEncoding
{
get
{
return _encoding;
}
set
{
OnPropertyChanged(ref _encoding, value);
}
}
public LineEnding LineEnding
{
get
{
return _lineEnding;
}
set
{
OnPropertyChanged(ref _lineEnding, value);
}
}
public string TabHeader
{
get
{
return string.IsNullOrEmpty(FileName) ? "Untitled Document" : FileName;
}
}
public bool IsSaved
{
get
{
return _isSaved;
}
set
{
OnPropertyChanged(ref _isSaved, value);
}
}
public bool IsInvalidFile
{
get
{
return (string.IsNullOrEmpty(FilePath) || string.IsNullOrEmpty(FileName));
}
}
public override bool Equals(object obj)
{
if (ReferenceEquals(obj, null))
return false;
if (ReferenceEquals(this, obj))
return true;
var comp = (TextDocument)obj;
return this.DocumentId == comp.DocumentId;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged<T>(ref T property, T value, [CallerMemberName] string propertyName = "")
{
property = value;
var handler = PropertyChanged;
if (handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
And this is my XAML Code for TabView:
<TabView x:Name="MyTabView" AddTabButtonClick="TabView_AddTabButtonClick" TabCloseRequested="TabView_TabCloseRequested"
SelectionChanged="TabView_SelectionChanged"
Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Background="White"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
TabItemsChanged="TabView_TabItemsChanged"
SelectedIndex="0"
TabItemsSource="{x:Bind MyDocuments,Mode=OneWay}"
>
<TabView.TabItemTemplate>
<DataTemplate x:DataType="viewmodels:TextDocument">
<TabViewItem Header="{x:Bind TabHeader,Mode=OneWay}" IconSource="{x:Null}">
<TabViewItem.Content>
<TextBox x:Name="TextBoxInsideTab" Grid.Column="0" Grid.Row="0"
PlaceholderText="Drag and drop a file here or start typing"
Text="{x:Bind TextContent,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="24"
UseSystemFocusVisuals="False"
BorderThickness="0"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
TextWrapping="Wrap"
IsSpellCheckEnabled="False"
CanBeScrollAnchor="True"
TextChanged="TextBox_TextChanged"
AcceptsReturn="True"
IsTabStop="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
/>
</TabViewItem.Content>
</TabViewItem>
</DataTemplate>
</TabView.TabItemTemplate>
</TabView>
And this is my C# code:
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using MyApp.ViewModels;
using Windows.Storage.Pickers;
using Windows.Storage;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace MyApp
{
public sealed partial class MainPage : Page
{
private ObservableCollection<TextDocument> MyDocuments;
public MainPage()
{
this.InitializeComponent();
MyDocuments = new ObservableCollection<TextDocument>()
{
new TextDocument()
};
}
private void TabView_AddTabButtonClick(TabView sender, object args)
{
AddTabViewItemDefault(sender.TabItems.Count);
}
private void AddTabViewItemDefault(int index)
{
MyDocuments.Add(new TextDocument());
}
private void TabView_TabCloseRequested(TabView sender, TabViewTabCloseRequestedEventArgs args)
{
MyDocuments.Remove(args.Item as TextDocument);
}
private void TabView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
}
}
}
ObservableCollection<T> and INotifyCollectionChanged currently don't work in UWP apps.
You need to implement your own custom collection as a workaround:
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Interop;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using NotifyCollectionChangedAction = Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction;
public class CustomObservableCollection<T> : Collection<T>, Microsoft.UI.Xaml.Interop.INotifyCollectionChanged, INotifyPropertyChanged
{
private ReentrancyGuard reentrancyGuard = null;
private class ReentrancyGuard : IDisposable
{
private CustomObservableCollection<T> owningCollection;
public ReentrancyGuard(CustomObservableCollection<T> owningCollection)
{
owningCollection.CheckReentrancy();
owningCollection.reentrancyGuard = this;
this.owningCollection = owningCollection;
}
public void Dispose()
{
owningCollection.reentrancyGuard = null;
}
}
public CustomObservableCollection() : base() { }
public CustomObservableCollection(IList<T> list) : base(list.ToList()) { }
public CustomObservableCollection(IEnumerable<T> collection) : base(collection.ToList()) { }
public event Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventHandler CollectionChanged;
public void Move(int oldIndex, int newIndex)
{
MoveItem(oldIndex, newIndex);
}
protected IDisposable BlockReentrancy()
{
return new ReentrancyGuard(this);
}
protected void CheckReentrancy()
{
if (reentrancyGuard != null)
{
throw new InvalidOperationException("Collection cannot be modified in a collection changed handler.");
}
}
protected override void ClearItems()
{
CheckReentrancy();
TestBindableVector<T> oldItems = new TestBindableVector<T>(this);
base.ClearItems();
OnCollectionChanged(
NotifyCollectionChangedAction.Reset,
null, oldItems, 0, 0);
}
protected override void InsertItem(int index, T item)
{
CheckReentrancy();
TestBindableVector<T> newItem = new TestBindableVector<T>();
newItem.Add(item);
base.InsertItem(index, item);
OnCollectionChanged(
NotifyCollectionChangedAction.Add,
newItem, null, index, 0);
}
protected virtual void MoveItem(int oldIndex, int newIndex)
{
CheckReentrancy();
TestBindableVector<T> oldItem = new TestBindableVector<T>();
oldItem.Add(this[oldIndex]);
TestBindableVector<T> newItem = new TestBindableVector<T>(oldItem);
T item = this[oldIndex];
base.RemoveAt(oldIndex);
base.InsertItem(newIndex, item);
OnCollectionChanged(
NotifyCollectionChangedAction.Move,
newItem, oldItem, newIndex, oldIndex);
}
protected override void RemoveItem(int index)
{
CheckReentrancy();
TestBindableVector<T> oldItem = new TestBindableVector<T>();
oldItem.Add(this[index]);
base.RemoveItem(index);
OnCollectionChanged(
NotifyCollectionChangedAction.Remove,
null, oldItem, 0, index);
}
protected override void SetItem(int index, T item)
{
CheckReentrancy();
TestBindableVector<T> oldItem = new TestBindableVector<T>();
oldItem.Add(this[index]);
TestBindableVector<T> newItem = new TestBindableVector<T>();
newItem.Add(item);
base.SetItem(index, item);
OnCollectionChanged(
NotifyCollectionChangedAction.Replace,
newItem, oldItem, index, index);
}
protected virtual void OnCollectionChanged(
NotifyCollectionChangedAction action,
IBindableVector newItems,
IBindableVector oldItems,
int newIndex,
int oldIndex)
{
OnCollectionChanged(new Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs(action, newItems, oldItems, newIndex, oldIndex));
}
protected virtual void OnCollectionChanged(Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs e)
{
using (BlockReentrancy())
{
CollectionChanged?.Invoke(this, e);
}
}
#pragma warning disable 0067 // PropertyChanged is never used, raising a warning, but it's needed to implement INotifyPropertyChanged.
public event PropertyChangedEventHandler PropertyChanged;
#pragma warning restore 0067
}
public class TestBindableVector<T> : IList<T>, IBindableVector
{
IList<T> implementation;
public TestBindableVector() { implementation = new List<T>(); }
public TestBindableVector(IList<T> list) { implementation = new List<T>(list); }
public T this[int index] { get => implementation[index]; set => implementation[index] = value; }
public int Count => implementation.Count;
public virtual bool IsReadOnly => implementation.IsReadOnly;
public void Add(T item)
{
implementation.Add(item);
}
public void Clear()
{
implementation.Clear();
}
public bool Contains(T item)
{
return implementation.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
implementation.CopyTo(array, arrayIndex);
}
public IEnumerator<T> GetEnumerator()
{
return implementation.GetEnumerator();
}
public int IndexOf(T item)
{
return implementation.IndexOf(item);
}
public void Insert(int index, T item)
{
implementation.Insert(index, item);
}
public bool Remove(T item)
{
return implementation.Remove(item);
}
public void RemoveAt(int index)
{
implementation.RemoveAt(index);
}
IEnumerator IEnumerable.GetEnumerator()
{
return implementation.GetEnumerator();
}
public object GetAt(uint index)
{
return implementation[(int)index];
}
public IBindableVectorView GetView()
{
return new TestBindableVectorView<T>(implementation);
}
public bool IndexOf(object value, out uint index)
{
int indexOf = implementation.IndexOf((T)value);
if (indexOf >= 0)
{
index = (uint)indexOf;
return true;
}
else
{
index = 0;
return false;
}
}
public void SetAt(uint index, object value)
{
implementation[(int)index] = (T)value;
}
public void InsertAt(uint index, object value)
{
implementation.Insert((int)index, (T)value);
}
public void RemoveAt(uint index)
{
implementation.RemoveAt((int)index);
}
public void Append(object value)
{
implementation.Add((T)value);
}
public void RemoveAtEnd()
{
implementation.RemoveAt(implementation.Count - 1);
}
public uint Size => (uint)implementation.Count;
public IBindableIterator First()
{
return new TestBindableIterator<T>(implementation);
}
}
public class TestBindableVectorView<T> : TestBindableVector<T>, IBindableVectorView
{
public TestBindableVectorView(IList<T> list) : base(list) { }
public override bool IsReadOnly => true;
}
public class TestBindableIterator<T> : IBindableIterator
{
private readonly IEnumerator<T> enumerator;
public TestBindableIterator(IEnumerable<T> enumerable) { enumerator = enumerable.GetEnumerator(); }
public bool MoveNext()
{
return enumerator.MoveNext();
}
public object Current => enumerator.Current;
public bool HasCurrent => enumerator.Current != null;
}
Page:
public sealed partial class MainPage : Page
{
private CustomObservableCollection<TextDocument> MyDocuments;
public MainPage()
{
this.InitializeComponent();
MyDocuments = new CustomObservableCollection<TextDocument>()
{
new TextDocument()
};
}
...
}
I think your code in the constructor might be breaking the initial binding to the ObservableCollection. Try this code:
private ObservableCollection<TextDocument> MyDocuments {get;} = new ObservableCollection<TextDocument>();
public MainPage()
{
this.InitializeComponent();
MyDocuments.Add(new TextDocument());
}
Does it help?
I have used ShellRenderer but I am unable to get the code working to add an extra space between the bottom of the screen and the icons. Here is an image:
I need to increase the space between the bottom of the screen and the tab item titles.
Here is the code that I have tried:-
namespace MyProject.Droid.CustomRenderers
{
public class CustomShellRenderer : ShellRenderer
{
public CustomShellRenderer(Context context) : base(context)
{
}
protected override IShellTabLayoutAppearanceTracker CreateTabLayoutAppearanceTracker(ShellSection shellSection)
{
return new CustomBottomNavAppearance();
}
protected override IShellSectionRenderer CreateShellSectionRenderer(ShellSection shellSection)
{
return base.CreateShellSectionRenderer(shellSection);
}
protected override IShellItemRenderer CreateShellItemRenderer(ShellItem shellItem)
{
return base.CreateShellItemRenderer(shellItem);
}
}
public class CustomBottomNavAppearance : IShellTabLayoutAppearanceTracker
{
public void Dispose()
{
}
public void ResetAppearance(TabLayout tabLayout)
{
}
public void SetAppearance(TabLayout tabLayout, ShellAppearance appearance)
{
for (int i = 0; i < tabLayout.ChildCount; i++)
{
var child = tabLayout.GetChildAt(i);
child.SetPadding(0, 0, 0, 20);
}
}
}
}
Let me know how can I fix this. Thanks!
I have created a UserControl which extends PictureBox Control
public partial class AudioMonitor : PictureBox
{
private SelectionSettings _selectionSettings;
[Description("Various settings regarding to the selection visuals"), Category("Custom")]
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public SelectionSettings SelectionSettings
{
get { return this._selectionSettings; }
set { this._selectionSettings = value; }
}
}
SelectionSettings property is a custom class which I have created as follows:
[Serializable]
public class SelectionSettings
{
private SelectionMarker _startMarker;
private SelectionMarker _endMarker;
private SelectionPen _selectionStyle;
public SelectionMarker StartMarker
{
get { return this._startMarker; }
set { this._startMarker = value; }
}
public SelectionMarker EndMarker
{
get { return this._endMarker; }
set { this._endMarker = value; }
}
public SelectionPen SelectionStyle
{
get { return this._selectionStyle; }
set { this._selectionStyle = value; }
}
}
[Serializable]
public class SelectionMarker
{
private Color _color = Color.White;
private DashStyle _style = DashStyle.Solid;
private float _width = 1.0F;
public Color Color
{
get { return this._color; }
set { this._color = value; }
}
public DashStyle Style
{
get { return this._style; }
set { this._style = value; }
}
public float Width
{
get { return this._width; }
set { this._width = value; }
}
public Pen Pen
{
get
{
Pen pen = new Pen(this._color);
pen.DashStyle = this._style;
pen.Width = this._width;
return pen;
}
}
}
[Serializable]
public class SelectionPen
{
private Color _color = Color.White;
private DashStyle _style = DashStyle.Solid;
private float _width = 1.0F;
private float _alpha = 100;
public Color Color
{
get { return this._color; }
set { this._color = value; }
}
public DashStyle Style
{
get { return this._style; }
}
public float Width
{
get { return this._width; }
}
public float Alpha
{
get { return this._alpha; }
}
public int AlphaPercent
{
get { return (int)Math.Round(this._alpha * 100 / 255); }
set
{
if (value > 0 && value <= 100)
this._alpha = (value * 255 / 100);
else
throw new ArgumentException("Alpha percentage should be between (0, 100]");
}
}
public Pen Pen
{
get
{
Pen pen = new Pen(Color.FromArgb((byte)this._alpha, this._color.R, this._color.G, this._color.B));
pen.DashStyle = this._style;
pen.Width = this._width;
return pen;
}
}
}
When I place my custom control on a Form and open the Properties Window I can see it as follows :
As you can see I can not set "SelectionSettings" property from the "Properties" window at the design time. What I need is to place the "..." button next to the Property name and open a pop-up to set values.
It should look something like this :
How can I accomplish this task?
What you want to do is add an Editor to AudioMonitor's SelectionSettings property.
To do this, you should create a custom class derived from the UITypeEditor Class.
In order to inherit from UITypeEditor, your project must reference System.Design, which can be done by going to the Project menu, selecting Add Reference to bring up the Reference Manager, navigating to Assemblies->Framework on the left panel and making sure System.Design is checked in the list.
In your custom UITypeEditor derived class, override the method EditValue to bring up a custom dialog that edits a value of type SelectionSettings. Then set the Editor Attribute on your SelectionSettings property to your custom UITypeEditor derived class.
Here's a generic code example of how this would look:
using System;
using System.Drawing.Design;
using System.ComponentModel;
using System.Windows.Forms;
// ...
[Editor(typeof(SomeProperty_Editor), typeof(UITypeEditor))] // You might be able to place this attribute on class SomeType, but I haven't tried yet
public SomeType SomeProperty
{
get { /* stuff */ }
set { /* stuff */ } // optional, really
}
class SomeProperty_Editor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
IWindowsFormsEditorService service = (IWindowsFormsEditorService)(provider.GetService(typeof(IWindowsFormsEditorService)));
SomeProperty_EditorWindow EditorWindow = new SomeProperty_EditorWindow(value as SomeType);
service.ShowDialog(EditorWindow);
if (EditorWindow.EditCancelled)
return value;
else
return EditorWindow.GetEdittedValue();
}
}
class SomeProperty_EditorWindow : Form
{
public SomeProperty_EditorWindow(SomeType CurrentProperty) : base()
{
InitializeComponents();
// Grab info in CurrentProperty here and display it on form
}
public void InitializeComponents()
{
// write yourself or use designer
}
public SomeType GetEdittedValue()
{
// return editted value from form components
}
public bool EditCancelled = false; // Set true if cancel button hit
}
You may want to look up the PropertyGrid Control as it could be very useful in your EditorWindow. Also, you can go to the UITypeEditor Class's MSDN page, look at all of the .NET derived classes under "Inheritance Hierarchy" to see if there is a built in editor that is close in functionality to what you want to do and inherit directly from that class.
I'm developing Pong with MvvmCross. The Paddles Y values change when holding the up and down buttons (buttons in the android view/activity - not keyboard buttons). However this is not being shown in the view (paddles stay in one spot, even though I can see in the console logs that the paddle has gone up or down).
Why are the paddles Y values not properly bound to the view?
Here is the code:
ViewModel:
using System.Linq;
using System.Text;
using System.Threading;
using Cirrious.MvvmCross.ViewModels;
using Pong.Core.ViewModels;
using Pong.Core.Models;
namespace Pong.Core.ViewModels
{
public class GamePlayViewModel
: MvxViewModel
{
private string _hello = "Hello MvvmCross";
public string Hello
{
get { return _hello; }
set { _hello = value; RaisePropertyChanged(() => Hello); }
}
private int _totalFramesBeenHad;
public int TotalFramesBeenHad
{
get { return _totalFramesBeenHad; }
set { _totalFramesBeenHad = value; RaisePropertyChanged(() => TotalFramesBeenHad); }
}
private PlayerPaddle _paddle1;
public int Paddle1
{
get { return _paddle1.Y; }
set { _paddle1.Y = value; RaisePropertyChanged(() => Paddle1); }
}
private ComputerPaddle _paddle2;
public int Paddle2
{
get { return _paddle2.Y; }
set { _paddle2.Y = value; RaisePropertyChanged(() => Paddle2); }
}
protected StandardBall StandardBall;
public GamePlayViewModel()
{
_paddle1 = new PlayerPaddle();
_paddle2 = new ComputerPaddle();
StandardBall = new StandardBall();
}
public void UpdatePaddle1()
{
switch (_paddle1.DetectWallCollision())
{
case "upper":
_paddle1.UpperWallHit();
break;
case "lower":
_paddle1.LowerWallHit();
break;
case "none":
_paddle1.MoveOneFrame();
break;
}
}
public void UpdateBall()
{
if (StandardBall.DetectWallCollision()) StandardBall.HandleWallCollision();
StandardBall.MoveOneFrame();
}
public void SetPaddleDirection(string direction)
{
_paddle1.SetDirection(direction);
}
public void StopPaddle()
{
_paddle1.StopMoving();
}
}
}
child viewmodel (actual one used):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Pong.Core.ViewModels;
using Pong.Core.Models;
using Pong.Droid.Views;
namespace Pong.Droid.ViewModels
{
public class GamePlayViewModelAndroid : GamePlayViewModel
{
public readonly Timer _dispatcherTimer;
public GamePlayView gpv;
public GamePlayViewModelAndroid (GamePlayView gpv)
{
this.gpv = gpv;
TimerCallback timerDelegate = new TimerCallback (Tick);
_dispatcherTimer = new Timer (timerDelegate, null, 0, 1000/Court.FPS);
}
public void Tick(object state)
{
UpdatePaddle1();
gpv.move ();
}
}
}
View:
using Android.App;
using Android.OS;
using Cirrious.MvvmCross.Droid.Views;
using Cirrious;
using Cirrious.CrossCore;
using Cirrious.MvvmCross.Binding;
using Cirrious.MvvmCross.ViewModels;
using Pong.Droid.ViewModels;
using Android.Content.PM;
using Pong.Droid;
using Android.Views;
using Android.Widget;
using Android.Graphics;
using Android.Content;
using Android.Content.Res;
using Cirrious.MvvmCross.Binding.BindingContext;
using Pong.Core.Models;
namespace Pong.Droid.Views
{
[Activity(Label = "!PONG!", ScreenOrientation = ScreenOrientation.Landscape)]
public class GamePlayView : MvxActivity
{
private GamePlayViewModelAndroid _viewModel;
private Button _buttonUp;
private Button _buttonDown;
public GameView GameView;
public LinearLayout ParentLayout;
public LinearLayout ButtonsLayout;
public int _paddle1y;
public int _paddle2y;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
GameView = new GameView (this);
_viewModel = new GamePlayViewModelAndroid(this);
SetUpControls ();
SetUpButtonEvents ();
SetContentView(ParentLayout);
DataContext = _viewModel;
this.ClearAllBindings();
var set = this.CreateBindingSet<GamePlayView, GamePlayViewModelAndroid>();
set.Bind(this).For(v => v._paddle1y).To(vm => vm.Paddle1);
set.Bind(this).For(v => v._paddle2y).To(vm => vm.Paddle2);
set.Apply();
}
void SetUpButtonEvents ()
{
_buttonUp.Touch += (s, e) => {
var handled = false;
if (e.Event.Action == MotionEventActions.Down) {
_viewModel.SetPaddleDirection ("up");
handled = true;
}
else
if (e.Event.Action == MotionEventActions.Up) {
_viewModel.StopPaddle ();
handled = true;
}
e.Handled = handled;
};
_buttonDown.Touch += (s, e) => {
var handled = false;
if (e.Event.Action == MotionEventActions.Down) {
_viewModel.SetPaddleDirection ("down");
handled = true;
}
else
if (e.Event.Action == MotionEventActions.Up) {
_viewModel.StopPaddle ();
handled = true;
}
e.Handled = handled;
};
}
void SetUpControls ()
{
_buttonUp = new Button (this);
_buttonDown = new Button (this);
ParentLayout = new LinearLayout (this);
ButtonsLayout = new LinearLayout (this);
ParentLayout.Orientation = Android.Widget.Orientation.Horizontal;
ButtonsLayout.Orientation = Android.Widget.Orientation.Vertical;
ButtonsLayout.AddView (_buttonUp);
ButtonsLayout.AddView (_buttonDown);
ParentLayout.AddView (ButtonsLayout);
ParentLayout.AddView (GameView);
}
public void move() {
//GameView.paddle1y = _viewModel.Paddle1.Y;
//GameView.paddle2y = _viewModel.Paddle2.Y;
RunOnUiThread (() => GameView.Invalidate ());
}
}
public class GameView : View {
private Bitmap _paddleBmp;
private int _paddle1x;
public int _paddle1y;
private int _paddle2x;
public int _paddle2y;
public GamePlayViewModelAndroid vm;
public GamePlayView View;
public GameView(Context context) : base (context) {
SetPaddleBmp ();
// this.ClearAllBindings();
// var set = this.CreateBindingSet<GameView, GamePlayViewModelAndroid>();
// set.Bind(_paddle1y).To(vm => vm.Paddle1.Y);
// set.Bind(_paddle2y).To(vm => vm.Paddle2.Y);
// set.Apply();
//var set = this.CreateBindingSet<PolicySummaryCell, PolicyComponent<BasePolicy>>();
//set.Bind(_periodOfInsurance).To(vm => vm.PeriodOfInsurance);
//set.Bind(_title).To(vm => vm.Title);
View = (GamePlayView)context;
}
void SetPaddleBmp ()
{
var paddlebmpTemp = BitmapFactory.DecodeResource (Resources, Resource.Drawable.Icon);
_paddleBmp = Bitmap.CreateScaledBitmap (paddlebmpTemp, Paddle.Width, Paddle.Height, false);
}
protected override void OnDraw(Canvas canvas) {
canvas.DrawColor(Color.Aqua);
canvas.DrawBitmap (_paddleBmp, _paddle1x, View._paddle1y, null);
canvas.DrawBitmap (_paddleBmp, _paddle2x, View._paddle2y, null);
}
protected override void OnSizeChanged(int w, int h, int oldw, int oldh) {
SetUpCourt (w, h);
}
void SetUpCourt (int w, int h)
{
Court.Width = w;
Court.Height = h;
Court.UpperBound = 0;
Court.LowerBound = h;
Court.LeftBound = 0;
Court.RightBound = w;
ComputerPaddle.X = Court.RightBound - Paddle.Width - 20;
_paddle2x = ComputerPaddle.X;
_paddle1x = PlayerPaddle.X;
}
}
}
Model:
using System.Diagnostics;
namespace Pong.Core.Models
{
public class Paddle
{
public int Y { get; set; }
public int VY { get; set; }
public static readonly int Speed = 600;
public static readonly int Height = 300;
public static readonly int Width = 100;
public void StopMoving()
{
VY = 0;
}
public void SetDirection(string direction)
{
if (direction == "up")
{
VY = -Speed;
}
else if (direction == "down")
{
VY = Speed;
}
}
public string DetectWallCollision()
{
if (Y < Court.UpperBound)
{
return "upper";
}
if (Y > (Court.LowerBound - Paddle.Height))
{
return "lower";
}
return "none";
}
public void UpperWallHit()
{
StopMoving();
Y = Court.UpperBound;
Debug.WriteLine("You hit the top wall");
}
public void LowerWallHit()
{
StopMoving();
Y = Court.LowerBound - Paddle.Height;
Debug.WriteLine("You hit the bottom wall");
}
public void MoveOneFrame()
{
Y += VY/Court.FPS;//this should trigger the RaisePropertyChanged(() => Paddle1)
}
}
public class PlayerPaddle : Paddle {
public static readonly int X = 20;
}
public class ComputerPaddle : Paddle {
public static int X;
}
}
I think your problem here, is that you're updating the Paddle.Y field, but the RaisePropertyChanged() called as your update the Paddle (on set).
See the difference?
Only if you'll set new instance of Paddle1 and Paddle2 property in GamePlayViewModel the setter will be called:
Paddle1 = new Paddle(); //will call the setter of Paddle1 property
Paddle1.Y = 90; //would not call the setter property
What you need to do is to call RaisePropertyChanged when you update the X and Y values of Paddle1 and Paddle2 properties.
The answer to this question is that on the view, to bind a field to the viewModel programmatically, the field must be a property with a getter and setter. Just like this. In the view:
public int _paddle1y { get; set; }
public int _paddle2y { get; set; }
I don't know why this is the case. I think it's a bit of a bug in MvvmCross. But maybe there is a valid reason for it.
Instead of calling _paddle1 directly, call Paddle1 (and 2) so the RaisePropertyChanged event will get called.
I have designed a custom panel which can expand or collapse form at run time.
When I change its height from custom designed task, it does not update it.
Code of my control class:
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms.Design;
[Designer(typeof(MyControlDesigner))]
public partial class ExpandCollapsePanel : UserControl
{
private bool flag = false;
private Size size;
public int usrVerticalSize;
public ExpandCollapsePanel()
{
InitializeComponent();
}
[DefaultValueAttribute(true)]
public int SetVerticalSize
{
get
{
return usrVerticalSize;
}
set
{
usrVerticalSize = value;
}
}
Code of taskpanedesign class:
namespace ExpandCollapseFormLibrary
{
class CustomDialogue : ControlDesigner
{
private DesignerActionListCollection actionLists;
public override DesignerActionListCollection ActionLists
{
get
{
if (actionLists == null)
{
actionLists = new DesignerActionListCollection();
actionLists.Add(new MyActionListItem(this));
}
return actionLists;
}
}
}
internal class MyActionListItem : DesignerActionList
{
public MyActionListItem(ControlDesigner owner) : base(owner.Component)
{
}
public override DesignerActionItemCollection GetSortedActionItems()
{
var items = new DesignerActionItemCollection();
//items.Add(new DesignerActionTextItem("Hello world", "Misc"));
items.Add(new DesignerActionPropertyItem("Checked", "Vertical Drop Down Size"));
return items;
}
public int Checked
{
get { return ((ExpandCollapsePanel)base.Component).SetVerticalSize; }
set { ((ExpandCollapsePanel)base.Component).SetVerticalSize = value; }
}
}
}
When I change the value the Form1(where drag and dropped) designed class keep it permanently.
the SetVerticalSize property value of your custom pane's is really changed, but the problem is that the designer host does not know about it at all. To notify the designer host about your custom pane changing you should implement something like this (I suggest you read the IComponentChangeService MSDN article for more details):
int usrVerticalSize;
[DefaultValue(true)]
public int SetVerticalSize {
get { return usrVerticalSize; }
set {
FireChanging(); //changing notification
try {
usrVerticalSize = value;
}
finally { FireChanged(); } //changed notification
}
}
void FireChanging() {
IComponentChangeService service = GetComponentChangeService();
if(service != null)
service.OnComponentChanging(this, null);
}
void FireChanged() {
IComponentChangeService service = GetComponentChangeService();
if(service != null)
service.OnComponentChanged(this, null, null, null);
}
IComponentChangeService GetComponentChangeService() {
return GetService(typeof(IComponentChangeService)) as IComponentChangeService;
}