Skiasharp enables to manipulate touch events with the method:
private void OnTouch(object sender, SKTouchEventArgs args)
With this method I can handle only one touch point coordinates:
args.Location.X, args.Location.Y
But how can I detect multitouch event?
The Android native API for example enables you to manage two finger locations in order to detect multitouch.
How can I achieve that with SkiaSharp?
Ok, I found a solution for my question here is the code.
//Multitouch handling
Dictionary<long, SKPoint> dragDictionary = new Dictionary<long, SKPoint>();
private void OnTouch(object sender, SKTouchEventArgs e)
{
switch (e.ActionType)
{
case SKTouchAction.Pressed:
dragDictionary[e.Id] = e.Location;
break;
case SKTouchAction.Entered:
break;
case SKTouchAction.Moved:
if (dragDictionary.Keys.Count > 1)
{
dragDictionary[e.Id] = e.Location;
SKPoint? p1 = null;
SKPoint? p2 = null;
foreach (long key in dragDictionary.Keys)
{
if (p1 == null)
{
p1 = dragDictionary[key];
}
else if (p2 == null)
{
p2 = dragDictionary[key];
}
}
//MultiTouch handle
}
else
{
//SingleTouch handle
}
break;
case SKTouchAction.Released:
dragDictionary.Remove(e.Id);
break;
case SKTouchAction.Exited:
break;
}
// we have handled these events
e.Handled = true;
((SKCanvasView)sender).InvalidateSurface();
}
Related
I have added Xamarin GestureRecognizers on a SKCanvasView and overridden OnTouch method.
I have some implementations on SKTouchAction.Moved. In order to trigger it after SKTouchAction.Pressed on Android I set e.Handled = true;
Doing so, none of the Xamarin GestureRecognizers seems to be working.
Is there any way to make both of these events work together or any alternative way to achieve this requirement?
Here is my code sample.
public abstract class GestureContainer : SKCanvasView, IViewportable
{
public GestureContainer()
{
var doubleTapGesture = new TapGestureRecognizer();
doubleTapGesture.NumberOfTapsRequired = 2;
doubleTapGesture.Tapped += TapGesture_DoubleTapped;
GestureRecognizers.Add(doubleTapGesture);
var pinchGesture = new PinchGestureRecognizer();
pinchGesture.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add(pinchGesture);
}
private void TapGesture_DoubleTapped(object sender, EventArgs e)
{
//Double tap action here
}
public void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
//Pinch action here
}
protected override void OnTouch(SKTouchEventArgs e)
{
switch (e.ActionType)
{
case SKTouchAction.Moved:
{
//Move action here
}
break;
case SKTouchAction.Pressed:
{
//Move action here
}
break;
}
e.Handled = false; // Xamarin gesture works when I set this to false
InvalidateSurface();
}
}
Is there a way to multi-select in a Windows Tree View? Similar to the image below
I know that .NET currently doesn't have a multiselect treeview. It is treated as a wrapper around the win32 native treeview control. I would like to avoid the Treeview's Checkbox property if possible. Any suggestions is greatly appreciated!
Im gonna assume you're trying to avoid check boxes. Here is an example:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
treeView1.DrawMode = OwnerDrawText;
treeView1.DrawNode += treeView1_DrawNode;
treeView1.NodeMouseClick += treeView1_NodeMouseClick;
}
private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e) {
// Show checked nodes with an underline
using (SolidBrush br = new SolidBrush(e.Node.TreeView.BackColor))
e.Graphics.FillRectangle(br, e.Node.Bounds);
Font nodeFont = e.Node.NodeFont;
if (nodeFont == null) nodeFont = e.Node.TreeView.Font;
if (e.Node.Checked) nodeFont = new Font(nodeFont, FontStyle.Underline);
using (SolidBrush br = new SolidBrush(e.Node.TreeView.ForeColor))
e.Graphics.DrawString(e.Node.Text, nodeFont, br, e.Bounds);
if (e.Node.Checked) nodeFont.Dispose();
}
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) {
if (Control.ModifierKeys == Keys.Shift && e.Node.Parent != null) {
// Extend selection
bool check = false;
foreach (TreeNode node in e.Node.Parent.Nodes) {
if (node.Checked) check = true;
node.Checked = check;
if (node == e.Node) break;
}
}
else {
unselectNodes(treeView1.Nodes);
e.Node.Checked = true;
}
}
This question has been answered here but I'll briefly answer your question. While it is true that Native Treeview control does not allow multiple selection, you can derive a subclass from it and override its behaviors.
Example code:
checkNodes method:
private void checkNodes(TreeNode node, bool check)
{
foreach (TreeNode child in node.Nodes)
{
if (child.Checked == true)
{
MessageBox.Show(child.Text);
}
//MessageBox.Show(child.Text);
checkNodes(child, check);
}
}
Treeview method after check:
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
if (e.Action != TreeViewAction.Unknown)
{
if (busy) return;
busy = true;
try
{
TreeNode _node = e.Node;
checkNodes(e.Node, e.Node.Checked);
if (e.Node.Checked)
{
MessageBox.Show(e.Node.Text);
}
}
finally
{
busy = false;
}
}
}
It is not trivial to do so, however it can be done.
My Aim: I want to initiate a video call from the start.
My Problem: It is getting initiated into audio call and then it's turning into video after end user answers the call.
void ConversationManager_ConversationAdded_Video(object sender, ConversationManagerEventArgs e)
{
Console.WriteLine("Inside conversation added for Video");
if (e.Conversation.Modalities[ModalityTypes.AudioVideo].State != ModalityState.Notified)
{
if (e.Conversation.CanInvoke(ConversationAction.AddParticipant))
{
try
{
e.Conversation.ParticipantAdded += Conversation_ParticipantAdded_Video;
e.Conversation.AddParticipant(client.ContactManager.GetContactByUri(receipient));
}
catch (ItemAlreadyExistException ex)
{
}
}
}
}
void Conversation_ParticipantAdded_Video(object source, ParticipantCollectionChangedEventArgs data)
{
if (data.Participant.IsSelf != true)
{
if (((Conversation)source).Modalities[ModalityTypes.AudioVideo].CanInvoke(ModalityAction.Connect))
{
object[] asyncState = { ((Conversation)source).Modalities[ModalityTypes.AudioVideo], "CONNECT" };
try
{
((Conversation)source).Modalities[ModalityTypes.AudioVideo].ModalityStateChanged += _AVModality_ModalityStateChanged_Video;
Console.WriteLine("entered video Satheesh participant added");
// ((Conversation)source).Modalities[ModalityTypes.AudioVideo].BeginConnect(ModalityCallback, asyncState);
((Conversation)source).Modalities[ModalityTypes.AudioVideo].BeginConnect(ModalityCallback, asyncState);
//((Conversation)source).Modalities[ModalityTypes.AudioVideo].EndConnect(ModalityCallback, asyncState);
Thread.Sleep(6000);
Console.WriteLine(source);
Console.WriteLine("entered video participant added");
}
catch (LyncClientException lce)
{
throw new Exception("Lync Platform Exception on BeginConnect: " + lce.Message);
}
}
}
}
public void ModalityCallback(IAsyncResult ar)
{
Object[] asyncState = (Object[])ar.AsyncState;
try
{
if (ar.IsCompleted == true)
{
if (asyncState[1].ToString() == "RETRIEVE")
{
((AVModality)asyncState[0]).EndRetrieve(ar);
}
if (asyncState[1].ToString() == "HOLD")
{
((AVModality)asyncState[0]).EndHold(ar);
}
if (asyncState[1].ToString() == "CONNECT")
{
Console.WriteLine("inside connect method CONNECT");
((AVModality)asyncState[0]).EndConnect(ar);
}
if (asyncState[1].ToString() == "FORWARD")
{
((AVModality)asyncState[0]).EndForward(ar);
}
if (asyncState[1].ToString() == "ACCEPT")
{
((AVModality)asyncState[0]).Accept();
}
}
}
catch (LyncClientException)
{ }
}
public void _AVModality_ModalityStateChanged_Video(object sender, ModalityStateChangedEventArgs e)
{
Console.WriteLine(sender);
Console.WriteLine("entered video modality changed");
switch (e.NewState)
{
case ModalityState.Connected:
if (_VideoChannel == null)
{
_VideoChannel = ((AVModality)sender).VideoChannel;
_VideoChannel.StateChanged += new EventHandler<ChannelStateChangedEventArgs>(_VideoChannel_StateChanged);
}
if (_VideoChannel.CanInvoke(ChannelAction.Start))
{
Console.WriteLine("entered video modality changed123");
_VideoChannel.BeginStart(MediaChannelCallback, _VideoChannel);
}
break;
case ModalityState.OnHold:
break;
case ModalityState.Connecting:
case ModalityState.Forwarding:
break;
case ModalityState.Transferring:
break;
}
}
private void MediaChannelCallback(IAsyncResult ar)
{
((VideoChannel)ar.AsyncState).EndStart(ar);
}
I Think it is because you call _AVModality_ModalityStateChanged_Video in ParticipantAdded event.
that means that you start your video when the person you call (the new participant) joins the conversation. thats why it is getting initiated into audio call and then it's turning into video after end user answers the call.
The solution is to fire a new event : ConversationStateChangedEvent
this is where I did it and works fine :
/// <summary>
/// Handles event raised when New meetNow window change it's state
/// </summary>
private void _NewMeetNowConversation_StateChanged(object sender, ConversationStateChangedEventArgs e)
{
Conversation conference = (Conversation)sender;
switch(e.NewState)
{
case ConversationState.Active:
conference.Modalities[ModalityTypes.AudioVideo].ModalityStateChanged += AVConferenceModalityStateChanged;
conference.ParticipantAdded += ConfParticipantAdded;
break;
case ConversationState.Terminated: //conversation window completely closed
break;
case ConversationState.Inactive:
break;
case ConversationState.Parked:
break;
case ConversationState.Invalid:
break;
}
}
I learning Xamarin Android, and i want to implement Google SignIn... But i'm not to be able to do that. I just need to use Client ID? i catch some examples in Internet but nothing works... Can someone give a example? or step by step how i can do this in Xamarin?
Thank you!
My Code:
using Android.App;
using Android.Widget;
using Android.OS;
using Android.Gms.Common.Apis;
using Android.Gms.Common;
using System;
using Android.Gms.Plus;
using Android.Content;
using Android.Runtime;
using Android.Gms.Plus.Model.People;
namespace LoginGoogle
{
[Activity(Label = "LoginGoogle", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity, GoogleApiClient.IConnectionCallbacks,
GoogleApiClient.IOnConnectionFailedListener
{
private GoogleApiClient googleApiClient;
private SignInButton btnGooglePlus;
private ConnectionResult connectionResult;
private bool intentProgress;
private bool signInClick;
private bool infoPopulated;
private TextView lblName;
private TextView lblTagLine;
private TextView lblBraggingRights;
private TextView lblGender;
private TextView lblRelationship;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
btnGooglePlus = FindViewById<SignInButton>(Resource.Id.btnGooglePlus);
btnGooglePlus.Click += btnGooglePlus_Click;
lblName = FindViewById<TextView>(Resource.Id.lblName);
lblTagLine = FindViewById<TextView>(Resource.Id.lblTagLine);
lblBraggingRights = FindViewById<TextView>(Resource.Id.lblBraggingRights);
lblGender = FindViewById<TextView>(Resource.Id.lblGender);
lblRelationship = FindViewById<TextView>(Resource.Id.lblRelationship);
GoogleApiClient.Builder builder = new GoogleApiClient.Builder(this);
builder.AddConnectionCallbacks(this);
builder.AddOnConnectionFailedListener(this);
builder.AddApi(PlusClass.API);
builder.AddScope(PlusClass.ScopePlusProfile);
builder.AddScope(PlusClass.ScopePlusLogin);
//Build our GoogleApiClient
googleApiClient = builder.Build();
}
void btnGooglePlus_Click(object sender, EventArgs e)
{
if (!googleApiClient.IsConnecting)
{
signInClick = true;
resolveSigInError();
}
}
private void resolveSigInError()
{
if (!googleApiClient.IsConnected)
{
//No need to resolve errors
return;
}
if (connectionResult.HasResolution)
{
try
{
intentProgress = true;
StartIntentSenderForResult(connectionResult.Resolution.IntentSender, 0, null,
0, 0, 0);
} catch (Android.Content.IntentSender.SendIntentException e)
{
intentProgress = false;
googleApiClient.Connect();
}
}
}
protected override void OnActivityResult(int requestCode,
[GeneratedEnum] Result resultCode, Intent data)
{
if(requestCode == 0)
{
if(resultCode != Result.Ok)
{
signInClick = false;
}
intentProgress = false;
if (googleApiClient.IsConnecting)
{
googleApiClient.Connect();
}
}
}
protected override void OnStart()
{
base.OnStart();
googleApiClient.Connect();
}
protected override void OnStop()
{
base.OnStop();
if (googleApiClient.IsConnected)
{
googleApiClient.Disconnect();
}
}
public void OnConnected(Bundle connectionHint)
{
//Successful login
signInClick = false;
if (PlusClass.PeopleApi.GetCurrentPerson(googleApiClient) != null)
{
IPerson plusUser = PlusClass.PeopleApi.GetCurrentPerson(googleApiClient);
if (plusUser.HasDisplayName)
{
lblName.Text += plusUser.DisplayName;
}
if (plusUser.HasTagline)
{
lblTagLine.Text += plusUser.Tagline;
}
if (plusUser.HasBraggingRights)
{
lblBraggingRights.Text += plusUser.BraggingRights;
}
{
switch (plusUser.RelationshipStatus)
{
case 0:
lblRelationship.Text += "Single";
break;
case 1:
lblRelationship.Text += "In a relationship";
break;
case 2:
lblRelationship.Text += "Engaged";
break;
case 3:
lblRelationship.Text += "Married";
break;
case 4:
lblRelationship.Text += "It's complicated";
break;
case 5:
lblRelationship.Text += "In an open relationship";
break;
case 6:
lblRelationship.Text += "Widowed";
break;
case 7:
lblRelationship.Text += "In a domestic partnership";
break;
case 8:
lblRelationship.Text += "In a civil union";
break;
default:
lblRelationship.Text += "Unknown";
break;
}
}
if (plusUser.HasGender)
{
switch (plusUser.Gender)
{
case 0:
lblGender.Text += "Male";
break;
case 1:
lblGender.Text += "Female";
break;
case 2:
lblGender.Text += "Other";
break;
default:
lblGender.Text += "Unknown";
break;
}
infoPopulated = true;
}
}
}
public void OnConnectionSuspended(int cause)
{
throw new NotImplementedException();
}
public void OnConnectionFailed(ConnectionResult result)
{
if (intentProgress)
{
//Store the ConnectionResult so that we can use it later when the user
//clicks 'sign in'
connectionResult = result;
if (signInClick)
{
//The user has already clicked 'signin' so we attempt to resolve all
//errors until the user is signed in
resolveSigInError();
}
}
}
}
}
enter code here
Please refer to the xamarin sample for google sign in. In this sample you do not need Client ID. For more google sign in information please check the google development document.
I catch some examples in Internet but nothing works...
I think you may not authorized the app.
Download the sample and make sure you've authorized the app in the Google Developers Console before use.
Authorized the app
Open the link.
Create your project name(whatever you write) and add the package name for it. In this sample the package name is com.xamarin.signinquickstart.
Get your SHA-1 please refer to this link.
Build and deploy the demo app and sign in with google account.
Screen shot:
But i'm not to be able to do that. I just need to use Client ID?
How to use the Client ID login on
According to google document. I think you can try to login with client id by:
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn)
.RequestIdToken("YOUR_CLIENT_ID")
.RequestEmail()
.Build();
mGoogleApiClient = new GoogleApiClient.Builder (this)
.EnableAutoManage(mLoginFragment, failedHandler)
.AddApi (Auth.GOOGLE_SIGN_IN_API,gso)
.Build ()
Is there a way to conditionally load different user control as edit form based on some user action (Edit vs. Create)?
This is how I ended up doing it
protected void Page_Init(object sender, EventArgs e)
{
ctlBenefitLimitsGrid.MasterTableView.EditFormSettings.EditFormType = GridEditFormType.WebUserControl;
ctlBenefitLimitsGrid.ItemDataBound += BenefitLimitsGrid_ItemDataBound;
ctlBenefitLimitsGrid.ItemCommand += BenefitLimitsGrid_ItemCommand;
ctlBenefitLimitsGrid.MasterTableView.EditFormSettings.EditFormType = GridEditFormType.WebUserControl;
}
void BenefitLimitsGrid_ItemCommand(object source, GridCommandEventArgs e)
{
switch (e.CommandName)
{
case "Edit":
e.Item.OwnerTableView.IsItemInserted = false;
ctlBenefitLimitsGrid.MasterTableView.EditFormSettings.UserControlName =
#"UserControls/BenefitLimitEdit.ascx";
break;
case "Add":
{
InsertAddControl(e);
break;
}
case "Delete":
var benefitLimitId = Convert.ToInt32(e.CommandArgument);
Presenter.Delete(benefitLimitId);
Presenter.LoadView();
break;
case "Save":
IEditView item;
if (e.Item.GetType() == typeof(GridEditFormInsertItem))
{
item =
(IEditView)e.Item.FindControl(GridEditFormItem.EditFormUserControlID);
}
else
{
item = ((GridDataItem)e.Item).EditFormItem.FindControl(GridEditFormItem.EditFormUserControlID) as IEditView;
}
if (item != null && item.HasErrors)
{
e.Canceled = true;
return;
}
e.Item.Edit = false;
ctlBenefitLimitsGrid.MasterTableView.ClearEditItems();
Presenter.LoadView();
break;
case "SaveAndNew":
{
var benefitLimitCreate = (IBenefitLimitCreate)e.Item.FindControl(GridEditFormItem.EditFormUserControlID);
if (benefitLimitCreate.HasErrors)
{
e.Canceled = true;
return;
}
Presenter.LoadView();
InsertAddControl(e);
break;
}
}
}
private void InsertAddControl(GridCommandEventArgs e)
{
ctlBenefitLimitsGrid.MasterTableView.ClearEditItems();
ctlBenefitLimitsGrid.MasterTableView.EditFormSettings.UserControlName = #"UserControls/BenefitLimitCreate.ascx";
e.Item.OwnerTableView.InsertItem();
var insertedItem = e.Item.OwnerTableView.GetInsertItem();
var ctlBenefitLimitCreate = (BenefitLimitCreate)insertedItem.FindControl(GridEditFormItem.EditFormUserControlID);
ctlBenefitLimitCreate.Presenter.LoadView();
}
I think that there was an article in the Telerik online documentation (under Insert/Update/Delete -> HowTo chapter) which does exactly what you are after.
Dick