I'm porting an application from WPF to AvaloniaUI/.Net 6 to be able to support macOS.
In WPF I used this code to change the cursor:
public class WaitCursor : IDisposable
{
private readonly Cursor _previousCursor;
public WaitCursor()
{
_previousCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = Cursors.Wait;
}
public void Dispose()
{
Mouse.OverrideCursor = _previousCursor;
}
}
How can I get this to work on macOS?
The original question was also asked on the AvaloniaUI GitHub Discussion board, and has this answer there:
public abstract class BaseWindow<T> : ReactiveWindow<T> where T : ViewModelBase
{
protected BaseWindow()
{
this.WhenActivated((CompositeDisposable disposable) =>
{
this.ViewModel.WhenAnyValue(x => x.IsBusy)
.Do(UpdateCursor)
.Subscribe()
.DisposeWith(disposable);
});
}
private void UpdateCursor(bool show)
{
this.Cursor = show ? new Cursor(StandardCursorType.Wait) : Cursor.Default;
}
}
I just use this on an element.
Cursor="Hand"
Which is also a StyledProperty ... meaning you can data bind it. Which I don't think you could in WPF.
public static readonly StyledProperty<Cursor?> CursorProperty =
AvaloniaProperty.Register<InputElement, Cursor?>(nameof(Cursor), null, true);
Which is on InputElement so pretty low in the inhertance stack.
Related
I have a template class that is in its own namespace and that I add to my code with `
new InfoLabel()`{ Text = "abc" };
Note that this is just a very simple example and I have other template objects that don't just depend on one thing, for example an object with 2-3 labels.
Is there a way that I can apply Xamarin C# fluent to create a templated object?
Here is the simple example object that I have:
namespace Test
{
public class InfoLabel : Label
{
public InfoLabel()
{
SetDynamicResource(FontFamilyProperty, Const.Fonts.DefaultRegular);
SetDynamicResource(FontSizeProperty, Const.Fonts.InfoTextFontSize);
SetDynamicResource(TextColorProperty, Const.Colors.InfoLabelColor);
LineBreakMode = LineBreakMode.WordWrap;
VerticalOptions = LayoutOptions.Start;
HorizontalTextAlignment = TextAlignment.Start;
}
}
}
What I would like to know is how I can set up the same thing using the latest C# fluent standards?
Here is the way I think it might be done. I used a Build() method but I would appreciate if someone more skilled than me could tell me if I am doing it correctly as this is a big change from what I am used to:
namespace Test
{
public class InfoLabel
{
public InfoLabel()
{
Build();
}
void Build() =>
new Label
{
LineBreakMode = LineBreakMode.WordWrap,
}
.TextLeft()
.DynamicResources((Label.FontFamilyProperty, Const.Fonts.DefaultRegular),
(Label.FontSizeProperty, Const.Fonts.InfoTextFontSize),
(Label.TextColorProperty, Const.Colors.InfoLabelColor));
Here is another idea that I have:
namespace Test
{
public class InfoLabel : Label
{
public InfoLabel()
{
LineBreakMode = LineBreakMode.WordWrap;
Build();
}
void Build() =>
this.TextLeft()
.DynamicResources((Label.FontFamilyProperty, Const.Fonts.DefaultRegular),
(Label.FontSizeProperty, Const.Fonts.InfoTextFontSize),
(Label.TextColorProperty, Const.Colors.InfoLabelColor));
Note that I am using an extension method for the resources.
You could create the instance of label like following
public class InfoLabel : Label
{
static InfoLabel CreateDefaultLabel()
{
return new InfoLabel
{
LineBreakMode = LineBreakMode.WordWrap,
}
.TextLeft()
.DynamicResources((Label.FontFamilyProperty, Const.Fonts.DefaultRegular),
(Label.FontSizeProperty, Const.Fonts.InfoTextFontSize),
(Label.TextColorProperty, Const.Colors.InfoLabelColor));
}
}
var label = InfoLabel.CreateDefaultLabel();
For more details of the usage of markup you could check this blog .
I've currently faced a rather simple issue which eventually put me to a dead end. I am building an application that uses Xamarin Forms and want to change a masking character when user enters password from a bullet to an asterisk.
For entering password I'm using Entry control in Portable lib project in my content page (in VS2017 professional):
<Entry x:Name="Entry_Password" Placeholder="Password" IsPassword="True" />
I know that I probably should create a custom Renderer in Android project for this one, but would really appreciate how to do it for this specific purpose.
Am sure the converter answer will work , but as a personal preference i dont like it. this looks like a renderer job to me .
and here is how i would do it(example only in android because i dont have ios, but its fairly simple to implement it there)
Usage Xamarin forms
<controls:PasswordBox Placeholder="Password"/>
The Renderer (Android)
[assembly: ExportRenderer(typeof(PasswordBox), typeof(PasswordBoxRenderer))]
namespace PasswordAsterisk.Droid.Renderers
{
public class PasswordBoxRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.InputType = Android.Text.InputTypes.TextVariationPassword |
Android.Text.InputTypes.ClassText;
Control.TransformationMethod = new HiddenPasswordTransformationMethod();
}
}
}
internal class HiddenPasswordTransformationMethod : Android.Text.Method.PasswordTransformationMethod
{
public override Java.Lang.ICharSequence GetTransformationFormatted(Java.Lang.ICharSequence source, Android.Views.View view)
{
return new PasswordCharSequence(source);
}
}
internal class PasswordCharSequence : Java.Lang.Object, Java.Lang.ICharSequence
{
private Java.Lang.ICharSequence _source;
public PasswordCharSequence(Java.Lang.ICharSequence source)
{
_source = source;
}
public char CharAt(int index)
{
return '*';
}
public int Length()
{
return _source.Length();
}
public Java.Lang.ICharSequence SubSequenceFormatted(int start, int end)
{
return _source.SubSequenceFormatted(start, end);
}
public IEnumerator<char> GetEnumerator()
{
return _source.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _source.GetEnumerator();
}
}
}
full source code example in GitHub
An easyier way to do this would be to use a Converter to swap every letter in an asterisk and than when you request the value it is plain.
This is an interesting post that could help you with your problem: https://forums.xamarin.com/discussion/52354/is-there-a-way-to-partially-mask-an-entry-field-in-xamarin
It's imported to use oneway and not twoway!
Good luck
I'm making an app with using Xamarin.forms.
You might know forms' button is not enough to use as image button if you tried one.
So I use Image as a button and add gesturerecogniger. It's working fine.
Good thing is that I can use all Image's bindable property same like using Image. (like 'Aspect property' and else)
Only problem is that Android button has sound effect when it's pressed.
Mine doesn't have.
How to play default button sound on Android?
[another try]
I tried to make layout and put Image and empty dummy button on it.
But If I do this, I can't use any property of Image or Button unless I manually link it.
So I think it's not the right way.
Thanks.
Xamarin.Android:
var root = FindViewById<View>(Android.Resource.Id.Content);
root.PlaySoundEffect(SoundEffects.Click);
Android playSoundEffect(int soundConstant)
Xamarin.iOS
UIDevice.CurrentDevice.PlayInputClick();
Xamarin.Forms via Dependency Service:
public interface ISound
{
void KeyboardClick () ;
}
And then implement the platform specific function.
iOS:
public void KeyboardClick()
{
UIDevice.CurrentDevice.PlayInputClick();
}
Android:
public View root;
public void KeyboardClick()
{
if (root == null)
{
root = FindViewById<View>(Android.Resource.Id.Content);
}
root.PlaySoundEffect(SoundEffects.Click);
}
Xamarin Forms:
PCL interface:
interface ISoundService { void Click(); }
Click handler:
void Handle_OnClick(object sender, EventArgs e) {
DependencyService.Get<ISoundService>().Click();
}
Android:
public class MainActivity {
static MainActivity Instance { get; private set; }
OnCreate() {
Instance = this;
}
}
class SoundService : ISoundService {
public void Click() {
var activity = MainActivity.Instance;
var view = activity.FindViewById<View>(
Android.Resource.Id.Content);
view.PlaySoundEffect(SoundEffects.Click);
}
}
Take a look at the following:
MonoTouch.UIKit.IUIInputViewAudioFeedback
Interface that, together with the UIInputViewAudioFeedback_Extensions class, comprise the UIInputViewAudioFeedback protocol.
See Also: IUIInputViewAudioFeedback
https://developer.xamarin.com/api/type/MonoTouch.UIKit.IUIInputViewAudioFeedback/
You'll want something like this (untested):
public void SomeButtonFunction()
{
SomeBtn.TouchUpInside += (s, e) => {
UIDevice.CurrentDevice.PlayInputClick();
};
}
I just upgraded a project from an older version of MvvmCross to the latest version, and I'm having trouble with bindings.
I'm aware of the LinkerPleaseInclude hack, and it looks like (at least some of) the properties I'm using are listed there.
I'm concerned about my usage of the Views and ViewModels. Here's an example.
public partial class HomeView : MvxViewController<HomeViewModel>
...
public override void ViewDidLoad ()
{
base.ViewDidLoad();
this.CreateBinding(BudgetTrackerButton).To((HomeViewModel vm) => vm.BudgetTracker).Apply();
this.CreateBinding(LoginButton).For("Title").To ((HomeViewModel vm) => vm.LogMessage).Apply();
this.CreateBinding(LoginButton).To((HomeViewModel vm) => vm.Login).Apply();
this.CreateBinding(ContactApprisenButton).To((HomeViewModel vm) => vm.Contact).Apply();
this.CreateBinding(AboutApprisenButton).To((HomeViewModel vm) => vm.About).Apply();
this.CreateBinding(AboutThisAppButton).To((HomeViewModel vm) => vm.AboutApp).Apply();
this.CreateBinding(FAQsButton).To((HomeViewModel vm) => vm.FAQs).Apply();
this.CreateBinding(PrivacyPolicyButton).To((HomeViewModel vm) => vm.PrivacyPolicy).Apply();
}
public abstract class MvxViewController<T> : UIViewController, IMvxBindingContextOwner, IUIWrappable, MvvmCross.iOS.Views.IMvxIosView where T : ViewModelBase
...
}
protected MvvmCross.Core.ViewModels.IMvxViewModel _viewModel = null;
public MvvmCross.Core.ViewModels.IMvxViewModel ViewModel {
get {
if (_viewModel == null) {
_viewModel = MvvmCross.Platform.Mvx.Resolve<T> ();
}
return _viewModel;
}
set {
_viewModel = value;
}
}
public MvvmCross.Core.ViewModels.MvxViewModelRequest Request {
get;
set;
}
public object DataContext {
get {
return _viewModel;
}
set {
_viewModel = (MvvmCross.Core.ViewModels.IMvxViewModel)value;
}
}
protected MvvmCross.Binding.BindingContext.IMvxBindingContext _bindingContext;
public IMvxBindingContext BindingContext {
get {
if (_bindingContext == null) {
_bindingContext = new MvvmCross.Binding.BindingContext.MvxBindingContext ();
}
return _bindingContext;
}
set {
_bindingContext = value;
}
}
CreateBinding is from the MvxBindingContextOwnerExtensions.
When I hit CreateBinding, the view model has been made.
I understand DataContext to be the same as the ViewModel, and I only include it to conform to the MvvmCross.iOS.Views.IMvxIosView interface.
Am I missing a step somewhere? An interface?
Matching views to view models should happen automatically based on naming conventions, right? (They didn't.. in my case, I had to manually specify the mappings in my Setup class. Could be contributing to this problem.)
Strangely enough, this works (for the button title anyway, I haven't tested the other bindings, and I'm not interested in updating all the bindings across the entire application if the fix is simple):
var set = this.CreateBindingSet<HomeView, HomeViewModel>();
set.Bind(LoginButton).For("Title").To(vm => vm.LogMessage);
I can post more code if something else would be relevant. I'm also new to MvvmCross.
My problem is very simple, but all the options confuse me...
In my MEF/Prism-application, I want to attach a specific behavior to one specific region. The doumentation says, that you can do it that way:
IRegion region = regionManager.Region["Region1"];
region.Behaviors.Add("MyBehavior", new MyRegion());
But where should I put this? Is there some place, this is supposed to be done in a bootstrapper method? Currently, I am adding the behavior like this in the Loaded-event of the shell:
/// <summary>
/// Interaction logic for Shell.xaml
/// </summary>
[Export(typeof(Shell))]
public partial class Shell
{
[ImportingConstructor]
public Shell(IRegionManager regionManager, ElementViewInjectionBehavior elementViewInjectionBehavior)
{
InitializeComponent();
Loaded += (sender, args) =>
{
IRegion region = regionManager.Regions[RegionNames.ElementViewRegion];
region.Behaviors.Add("ElementViewInjection", elementViewInjectionBehavior);
};
}
}
Is this a good solution. I'd rather do it in the bootstrapper, so that it is done in the same place as the other region behavior registrations (ConfigureDefaultRegionBehaviors()).
So, the question: Where is the best place to add the behavior to one single region?
I just came up with a slightly improved solution, using a static string collection in the behavior to add the regions to attach the behavior to.
public class ViewModelInjectionBehavior : RegionBehavior, IDisposable
{
private static List<string> _regionNames;
public static List<string> Regions
{
get { return _regionNames ?? (_regionNames = new List<string>()); }
}
protected override void OnAttach()
{
if (Regions.Contains(Region.Name)) {...}
}
}
Then in my bootstrapper, I can define the regions:
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
var behaviorFactory = base.ConfigureDefaultRegionBehaviors();
ViewModelInjectionBehavior.Regions.Add(RegionNames.ElementViewRegion);
behaviorFactory.AddIfMissing("ElementViewInjectionBehavior", typeof(ViewModelInjectionBehavior));
return behaviorFactory;
}
At least, the behavior is universally usable now...
We had the same issue - in the end we just checked the region name in the region behaviour and acted only if it was that region that we wanted, kind of sucks because you are attaching the behaviour to all regions - but for us it was better than the suggested solution..
An example looks like :
public class TrackViewOpenerBehaviour : IRegionBehavior
{
public IRegion Region { get; set; }
public void Attach()
{
if (this.Region.Name == ApplicationRegions.WorkspaceRegion
|| this.Region.Name == ApplicationRegions.DialogRegion)
{
this.Region.Views.CollectionChanged += (sender, e) =>
{
//Code Here.
};
}
}
}
I always thought maybe we could create a behaviour that was responsible for attaching other behaviours to specfiic regions for us, then we could register that in the bootstrapper - but never got round to it.