Haptic Feedback crashes in Xamarin.Forms iOS Dependency Service - c#

I am trying to use haptic feedback in a Xamarin.Forms application to inform the user of selection changes. I found references to iOS and made a dependency service for iOS; however, it always crashes without any C# error. I've tried invoking it on the main thread and the thread I'm using. It even crashes in a try/catch block. Here is my code:
using System;
namespace App.Services
{
public enum HapticFeedbackType
{
ImpactHeavy, // Heavy impact
ImpactMedium, // Medium impact
ImpactLight, // Light impact
Selection, // To tick while scrolling through a scrollview or carousel
NotificationError, // When an in-app error notification occurs
NotificationWarning, // When an in-app warning notification occurs
NotificationSuccess // When an in-app success notification occurs
}
public interface IHapticFeedback
{
void PrepareHapticFeedback(HapticFeedbackType type);
void ExecuteHapticFeedback(HapticFeedbackType type);
}
}
using System;
using UIKit;
using Xamarin.Forms;
using App.Services;
[assembly: Dependency(typeof(App.iOS.Services.HapticFeedbackService))]
namespace App.iOS.Services
{
public class HapticFeedbackService : IHapticFeedback
{
HapticFeedbackHelper helper;
public HapticFeedbackService()
{
helper = new HapticFeedbackHelper();
}
public void PrepareHapticFeedback(HapticFeedbackType type)
{
helper.PrepareHapticFeedback(type);
}
public void ExecuteHapticFeedback(HapticFeedbackType type)
{
helper.ExecuteHapticFeedback(type);
}
}
//https://blog.francois.raminosona.com/add-vibrations-in-a-xamarin-ios-app/
public class HapticFeedbackHelper: IDisposable
{
private UIImpactFeedbackGenerator _impactHeavyFeedbackGenerator;
private UIImpactFeedbackGenerator _impactMediumFeedbackGenerator;
private UIImpactFeedbackGenerator _impactLightFeedbackGenerator;
private UISelectionFeedbackGenerator _selectionFeedbackGenerator;
private UINotificationFeedbackGenerator _notificationFeedbackGenerator;
public HapticFeedbackHelper()
{
_impactHeavyFeedbackGenerator = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Heavy);
_impactMediumFeedbackGenerator = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Medium);
_impactLightFeedbackGenerator = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Light);
_selectionFeedbackGenerator = new UISelectionFeedbackGenerator();
_notificationFeedbackGenerator = new UINotificationFeedbackGenerator();
}
public void PrepareHapticFeedback(HapticFeedbackType type)
{
switch (type)
{
case HapticFeedbackType.ImpactHeavy:
_impactHeavyFeedbackGenerator.Prepare();
break;
case HapticFeedbackType.ImpactMedium:
_impactMediumFeedbackGenerator.Prepare();
break;
case HapticFeedbackType.ImpactLight:
_impactLightFeedbackGenerator.Prepare();
break;
case HapticFeedbackType.Selection:
_selectionFeedbackGenerator.Prepare();
break;
case HapticFeedbackType.NotificationError:
case HapticFeedbackType.NotificationWarning:
case HapticFeedbackType.NotificationSuccess:
_notificationFeedbackGenerator.Prepare();
break;
}
}
public void ExecuteHapticFeedback(HapticFeedbackType type)
{
switch (type)
{
case HapticFeedbackType.ImpactHeavy:
_impactHeavyFeedbackGenerator.ImpactOccurred();
break;
case HapticFeedbackType.ImpactMedium:
_impactMediumFeedbackGenerator.ImpactOccurred();
break;
case HapticFeedbackType.ImpactLight:
_impactLightFeedbackGenerator.ImpactOccurred();
break;
case HapticFeedbackType.Selection:
_selectionFeedbackGenerator.SelectionChanged();
break;
case HapticFeedbackType.NotificationError:
_notificationFeedbackGenerator.NotificationOccurred(UINotificationFeedbackType.Error);
break;
case HapticFeedbackType.NotificationWarning:
_notificationFeedbackGenerator.NotificationOccurred(UINotificationFeedbackType.Warning);
break;
case HapticFeedbackType.NotificationSuccess:
_notificationFeedbackGenerator.NotificationOccurred(UINotificationFeedbackType.Success);
break;
}
}
#region IDisposable
public void Dispose()
{
_impactHeavyFeedbackGenerator = null;
_impactMediumFeedbackGenerator = null;
_impactLightFeedbackGenerator = null;
_selectionFeedbackGenerator = null;
_notificationFeedbackGenerator = null;
}
#endregion
}
}
async void LikeDown(object sender, MR.Gestures.DownUpEventArgs e)
{
LoggingController.Info("LikeDown event started...");
if (cancelReactionShowToken?.Token != null && cancelReactionShowToken.Token.CanBeCanceled)
cancelReactionShowToken.Cancel();
cancelReactionShowToken = new CancellationTokenSource();
Task task = new Task(async delegate {
await Task.Delay(800);
if (cancelReactionShowToken.Token.IsCancellationRequested)
{
cancelReactionShowToken = null;
return;
}
MainThread.BeginInvokeOnMainThread(() =>
{
reactionPopup = new SfPopupLayout();
reactionPopup.PopupView.ShowHeader = false;
reactionPopup.PopupView.ShowFooter = false;
reactionPopup.PopupView.AutoSizeMode = AutoSizeMode.Both;
reactionPopup.PopupView.ContentTemplate = new DataTemplate(() =>
{
ReactionsView view = new ReactionsView();
this.Emojis = view.Emojis;
this.ReactionTypes = view.ReactionTypes;
return view;
});
reactionPopup.ShowRelativeToView(actionsRow, RelativePosition.AlignBottom, 0, 0);
try
{
hapticFeedback.ExecuteHapticFeedback(Services.HapticFeedbackType.Selection);
} catch (Exception ex) { Console.WriteLine(ex.Message); }
//needs to be canceled so other guestures know
cancelReactionShowToken.Cancel();
}, cancelReactionShowToken.Token);
task.Start();
await task;
}
If anyone has any experience using haptic feedback in Xamarin.Forms it would help a lot.

You need to place an exception catch-point and see exactly where the crash happens. You have placed all your code and it's difficult to figure out where you messed up. It could just be in something unrelated. You should also share your application out put that will display what the exception is.
Here's an example of the Haptic implementation by ElysiumLab.com, they also have a Nuget package that possibly has it implemented for use "Naylah":
In your core/forms project
public class HapticFeedback
{
public static IHapticFeedback Instance { get; set; }
static HapticFeedback()
{
Instance = new DefaultHapticFeedback();
}
}
internal class DefaultHapticFeedback : IHapticFeedback
{
public void Run(HapticFeedbackType hapticFeedbackType)
{
//This is a default thing should not be used;
//throw new System.Exception("Not initialized in device platforms isbrubles");
}
}
public interface IHapticFeedback
{
void Run(HapticFeedbackType hapticFeedbackType);
}
public enum HapticFeedbackType
{
Softy,
Medium,
Heavy
}
iOS Implementation
public class HapticFeedbackService
{
public static void Init()
{
HapticFeedback.Instance = new iOSHapticFeedback();
}
}
public class iOSHapticFeedback : IHapticFeedback
{
public void Run(HapticFeedbackType hapticFeedbackType)
{
UIImpactFeedbackGenerator impact = null;
switch (hapticFeedbackType)
{
case HapticFeedbackType.Softy:
impact = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Light);
break;
case HapticFeedbackType.Medium:
impact = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Medium);
break;
case HapticFeedbackType.Heavy:
impact = new UIImpactFeedbackGenerator(UIImpactFeedbackStyle.Heavy);
break;
}
impact.Prepare();
impact.ImpactOccurred();
}
}
Btw, you can also create an Android implementation for the Haptic Feedback like this:
public class HapticFeedbackService
{
public static void Init(Activity activity)
{
HapticFeedback.Instance = new AndroidHapticFeedback(activity);
}
}
internal class AndroidHapticFeedback : IHapticFeedback
{
private readonly Activity activity;
public AndroidHapticFeedback(Activity activity)
{
this.activity = activity;
}
public void Run(HapticFeedbackType hapticFeedbackType)
{
switch (hapticFeedbackType)
{
case HapticFeedbackType.Softy:
activity.Window.DecorView.RootView.PerformHapticFeedback(FeedbackConstants.ContextClick);
break;
case HapticFeedbackType.Medium:
activity.Window.DecorView.RootView.PerformHapticFeedback(FeedbackConstants.KeyboardPress);
break;
case HapticFeedbackType.Heavy:
activity.Window.DecorView.RootView.PerformHapticFeedback(FeedbackConstants.KeyboardPress);
break;
}
}
}
You can check out the detailed code here, but if you are just looking for an iOS implementation you can see here.

Related

Calling a Command from a Separate Class File

So I have been at it for days, and for the life of me cannot find any documentation that fits my situation exactly here.
I have essentially set up a custom navigation service and would like to call the command from my ViewModel Class directly from my User Control.
I think I'm on the edge of having it here, but my lack of experience with C# is shooting me in the foot.
Here is the section of code from my Login.xaml.cs in question:
private LoginViewModel _loginViewModel;
public Login(LoginViewModel loginViewModel)
{
_loginViewModel = loginViewModel;
}
private void GrantAccess()
{
int userAccess = Int16.Parse(User.Access);
if (userAccess == 1)
{
MessageBox.Show("The bottom man");
}
if (userAccess == 2)
{
MessageBox.Show("The little boss");
}
if (userAccess == 3)
{
MessageBox.Show("The little big boss");
}
if (userAccess == 4)
{
{
_loginViewModel.NavigateMM1Command.Execute(null);
}
}
}
and here is the command I'm trying to reference from the ViewModel:
public class LoginViewModel : BaseViewModel
{
public ICommand NavigateMM1Command { get; }
public LoginViewModel(NavigationStore navigationStore)
{
NavigateMM1Command = new NavigateCommand<MM1ViewModel>(new NavigationService<MM1ViewModel>(navigationStore, () => new MM1ViewModel(navigationStore)));
}
}
Basically I've been going through tutorial after tutorial trying to apply what they teach to what I need and its worked for the most part but now _loginViewModel is throwing a null reference exception and I'm not sure why.
I have tried:
LoginViewModel loginViewModel = new loginViewModel();
but its asking me to pass a navigationStore argument through it and that feels wrong.
Any help here will cure my temporary insanity XD
You're receiving a Null Object Reference because navigationStore is null when LoginViewModel is being constructed.
That is, you have not configured a means to instantiate the type navigationStore when constructing LoginViewModel.
Dependency Injection (DI), or Invocation of Control (IoC) is bit more comprehensive a subject to cover in this answer.
Having said that,
I'll provide code to review here. It represents a means to configure a service provider using a collection of type mappings.
In this complete, ConsoleApp example, we'll explicitly instantiate a ServiceCollection, add Service Types (specifying mapping where application), and Build the ServiceProvider; With that provider, we'll resolve and instantiate Login type using GetService -- instantiating all the types;
The Types are essentially mockups of the types you've specified, but I've modified some aspects (an made up notioned like what your Execute method and usage of NavigationStore was).
DemoNavTypes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleDemo.NavLoginDemo
{
public interface ICommand
{
void Execute(string? userName);
}
public interface INavigationStore {
public bool this[string index] { get;set; }
}
public interface INavigationService {
void GrantAccessToUser(string userName);
}
public interface INavigationViewModel { }
internal class NavigationStore : INavigationStore
{
private Dictionary<string, bool> userAccessDict;
public NavigationStore() {
userAccessDict = new Dictionary<string, bool>();
}
public bool this[string index] {
get => userAccessDict.TryGetValue(index, out var val) && val;
set => userAccessDict[index] = value;
}
}
internal class NavigationService : INavigationService
{
private readonly INavigationStore _navigationStore;
public NavigationService(INavigationStore navigationStore)
{
_navigationStore = navigationStore;
}
public void GrantAccessToUser(string? userName)
{
if (string.IsNullOrWhiteSpace(userName))
throw new ArgumentException(nameof(userName));
_navigationStore[userName!] = true;
}
}
internal class NavigationCommand : ICommand
{
private readonly INavigationService _navigationService;
public NavigationCommand(INavigationService navigationService)
{
_navigationService = navigationService;
}
public void Execute(string? userName)
{
if (userName != null)
{
_navigationService.GrantAccessToUser(userName);
}
}
}
internal class User
{
public string? Name { get; set; }
public string Access { get; set; } = "1";
}
public abstract class BaseViewModel
{
internal User User { get; set; } = new User();
protected BaseViewModel() { }
}
internal class LoginViewModel : BaseViewModel, INavigationViewModel
{
private readonly ICommand _command;
public LoginViewModel(ICommand command) : base()
{
_command = command;
}
internal ICommand NavigateMM1Command => _command;
}
internal class Login
{
private User User => _loginViewModel.User;
private readonly LoginViewModel _loginViewModel;
public Login(LoginViewModel loginViewModel)
{
_loginViewModel = loginViewModel;
}
internal void SetAccess(int access)
{
SetAccess($"{access}");
}
internal void SetAccess(string access)
{
User.Access = access;
}
internal void SetUserName(string userName) { User.Name = userName; }
internal async Task GrantAccessAsync()
{
await Task.Yield();
int userAccess = Int16.Parse(User.Access);
switch (userAccess)
{
case 1:
Console.WriteLine("The bottom man");
break;
case 2:
Console.WriteLine("The little boss");
break;
case 3:
Console.WriteLine("The little big boss");
break;
case 4:
_loginViewModel.NavigateMM1Command.Execute(User.Name);
break;
default:
throw new NotImplementedException();
}
}
}
}
Program.cs (using Microsoft.Extensions.DependencyInjection)
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Immutable;
using System.ComponentModel.Design;
using System.Linq;
using ConsoleDemo.NavLoginDemo;
internal class Program
{
private static async Task Main(string[] args)
{
var services = new ServiceCollection();
var provder = ConfigureServices(services);
var login = provder.GetService<Login>();
if (login != null)
{
await login.GrantAccessAsync();
login.SetAccess(2);
await login.GrantAccessAsync();
login.SetAccess(3);
await login.GrantAccessAsync();
login.SetUserName("James Bond");
login.SetAccess(4);
await login.GrantAccessAsync();
}
}
private static IServiceProvider ConfigureServices(IServiceCollection services)
{
return services
.AddScoped<INavigationStore, NavigationStore>()
.AddScoped<INavigationService, NavigationService>()
.AddScoped<ICommand, NavigationCommand>()
.AddScoped<LoginViewModel>()
.AddScoped<Login>()
.BuildServiceProvider();
}
}
Note
-- However,
In your application, you'll probably already have a ServiceCollection instance in your Program.cs or Startup.cs file. And the ServiceProvider (or HostProvider) will be managed over by the application; So, you probably won't need to explicitly resolve (or GetService<T>) -- just add the Service Type (mappings) in ServiceCollection. Those parameter types will be instantiated and 'injected' into the constructor of Type that is itself being instantiated.

How to implement IDisposable pattern for a serial connection wrapper

I am designing a C# library that uses FTDI library to manage FTDI devices and their connections. I model it in three levels of operations:
1. Enumerate plugged devices;
2. Open/Close a connection to a specific device;
3. Write to the device, and receive bytes from it.
Currently I have the following classes:
public static class FtdiEnumerator
{
public static IEnumerable<FtdiDevice> Enumerate()
{
FTDI ftdi = new FTDI();
FTDI.FT_STATUS status;
uint bufferLenght = 0;
status = ftdi.GetNumberOfDevices(ref bufferLenght);
FTDI.FT_DEVICE_INFO_NODE[] result = new FTDI.FT_DEVICE_INFO_NODE[bufferLenght];
if (status != FTDI.FT_STATUS.FT_OK)
return Enumerable.Empty<FtdiDevice>();
status = ftdi.GetDeviceList(result);
if (status != FTDI.FT_STATUS.FT_OK)
return Enumerable.Empty<DispositivoFtdi>();
return result.Where(node => node != null)
.Select(node => new DispositivoFtdi(node))
.ToArray(); ;
}
}
public class FtdiDevice
{
protected FT_DEVICE_INFO_NODE _node;
protected FTDI _ftdi = new FTDI();
public string Description => _node.Description;
public string SerialNumber => _node.SerialNumber;
public FtdiDevice(FT_DEVICE_INFO_NODE node)
{
_node = node;
}
public void Open(uint baudRate = 115200)
{
FT_STATUS status = _ftdi.OpenBySerialNumber(_node.SerialNumber);
if (status != FT_STATUS.FT_OK)
throw new Exception();
status = _ftdi.SetBaudRate(baudRate);
if (status != FT_STATUS.FT_OK)
throw new Exception()
}
public void Close()
{
_ftdi.Close();
}
public void Write(byte[] bytes)
{
uint bytesReceived = 0;
_ftdi.Write(bytes, bytes.Length, ref bytesReceived);
}
}
I know about the IDisposable pattern, and I see a clear use case for it here, regarding FtdiDevice.Open() and FtdiDevice.Close() methods, but I see that usually the pattern is implemented with another, additional class. I imagine something like a FtdiConnection class, to be used like this, I think:
var device = new FtdiDevice(node);
using (FtdiConnection connection = device.Open())
{
connection.Write(bytes);
}
That would imply moving the Write(byte[] bytes) method to this FtdiConnection class.
I am not sure if I am in the right track, or how much sense my idea makes, and would appreciate any clarification.
I think what you want to do this:
public class FtdiConnection : IDisposable
{
private FtdiDevice device;
public FtdiConnection(FtdiDevice device)
{
this.device = device;
}
public void Dispose()
{
device.Close();
}
}
into your open method:
public FtdiConnection Open(uint baudRate = 115200)
{
... Open ...
return new FtdiConnection(this);
}
and use like
var device = new FtdiDevice(node);
using (FtdiConnection connection = device.Open())
{
connection.Write(bytes);
}
I found out, inspired on the discussion and previous answer, the following design answers my needs. I will add it here as an alternative to Gustavo's answer, and would be glad to know other alternatives.
The solution principle here is to have Device.Open() to return this as the disposable, thus avoiding the need for an additional Connection class:
using System;
namespace DisposableConnection
{
class Program
{
static void Main(string[] args)
{
var device = new Device();
using (device.Open())
{
device.Write();
}
Console.ReadKey();
}
}
public class Device : IDisposable
{
public Device()
{
}
public IDisposable Open()
{
Console.WriteLine("Open!!");
return this;
}
public void Close()
{
Console.WriteLine("Close!!");
}
internal void Write()
{
Console.WriteLine("Write!!");
//throw new Exception(); // optional, also works
}
public void Dispose()
{
Close();
}
}
}

Xamarin.iOS.UIKit.UIApplication.SendEvent gets called weirdly upon modal close

I'm deriving from UIApplication and overriding SendEvent to capture user taps.
Everything works fine, except when closing modals of type IMvxModalIosView.
Here's the method:
public override void SendEvent(UIEvent uievent)
{
NSSet touches = uievent.AllTouches;
if (touches != null)
{
UITouch touch = (UITouch)touches.AnyObject;
switch (touch.Phase)
{
case UITouchPhase.Ended:
StartDoingStuffAgain();
break;
default:
StopDoingStuff();
break;
}
}
base.SendEvent(uievent);
}
Debugging this while closing an IMvxModalIosView, if I set a breakpoint at the start of the method, as well as StartDoingStuffAgain(), the latter gets hit. If however I only set a breakpoint at StartDoingStuffAgain(), it never gets hit. StopDoingStuff() gets hit regardless.
Why is this?
Edit 1: As per nmilcoff's request for more code:
[Register("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate
{
//...
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
//...
_app = application as MyUIApplication;
//...
}
//...
}
[Register("MyUIApplication")]
public class MyUIApplication : UIApplication
{
//...
// Bit of code in original question goes here.
//...
}
//The iPad view presenter (iPhone has MyIPhoneViewPresenter)
public class MyIPadViewPresenter : MvxBaseIosViewPresenter
{
//...
private UINavigationController _navController;
private UINavigationController _modalNavController;
private IMvxMessenger _messenger;
protected readonly SemaphoreSlim _autoLock = new SemaphoreSlim(1, 1);
//...
public override async void Show(MvxViewModelRequest request)
{
await _autoLock.WaitAsync();
//...
if(_messenger == null)
{
_messenger = Mvx.Resolve<IMvxMessenger>();
}
// Among other things, the following method checks if the page
// corresponding to the VM request is a Xamarin.Forms or a native view.
var presentation = MvxIOSPresentationHelpers.CreateView(request, TargetIdiom.Tablet);
switch(presentation.Type)
{
//...
case ViewType.ModalWithNav:
showNavModalView(presentation.ViewController);
break;
//...
}
messenger.Publish(new ViewModelShowMessage(request.ViewModelType, presentation.ViewController, this));
//..
_autoLock.Release();
}
//...
private void showNavModalView(UIViewController viewController)
{
if(_modalNavController == null)
{
_modalNavController = new ModalNavController(viewController);
_modalNavController.ModalPresentationStyle = viewController.ModalPresentationStyle;
_navController.PresentViewController(_modalNavController, false, delegate { });
}
else
{
_modalNavController.PushViewController(viewController, true);
}
//...
}
//...
public override async void ChangePresentation(MvxPresentationHint hint)
{
var close_hint = hint as MvxClosePresentationHint;
if (close_hint != null)
{
await _autoLock.WaitAsync();
close(close_hint.ViewModelToClose);
_autoLock.Release();
}
else
{
//...
}
}
//...
protected void close(IMvxViewModel toClose)
{
//...
_messenger.Publish(new MvxMessage(this));
//...
if (_modalNavController != null)
{
if(_modalNavController.ChildViewControllers.Length <= 1)
{
// This is where the close sequence comes down to.
_modalNavController.DismissViewController(true, delegate{ });
_modalNavController = null;
}
else
{
//...
}
return;
}
//..
}
//...
}
// The VM corresponding to the IMvxModalIosView
public class MyProblematicModalVM : MvxViewModel
{
//...
public ICommand CloseCommand
{
get { return new MvxCommand(() => Close(this)); }
}
//...
}

storing a function template

I want to store some generic functions for later execution. The problem arises about the arguments of functions. For different types I want to create and store same generic function delegate, but I cannot do it. Below is my version of class to get functions;
public delegate void CGTaskHandler1<T>(T value) where T : IControllerBase;
public class CGTask
{
private CGTaskHandler1<IControllerBase> Aksiyon;
private IControllerBase param;
public void RegisterCGTask(CGTaskHandler1<IControllerBase> aFunc, IControllerBase aParam)
{
Aksiyon = aFunc;
param = aParam;
}
public void ExecuteCGTask()
{
try
{
Aksiyon(param);
}
catch (Exception ex)
{
Logger.SetLog("action execution failed ", LogType.error, ex.Message)
}
}
}
by this class I used an interface to collect every different type of argument under same name, however compiler wants exact same type and interface type seems not helping.
private void LoadScene(cScene ascn)
{
ascn.LoadScene();
}
public CGTask GetTask(String btnName)
{
CGTask back = new CGTask();
CGTaskHandler1<IControllerBase> alomelo = LoadScene; // type mismatch
back.RegisterCGTask(alomelo, thisScene);
//CGTask2<cScene> back = new CGTask2<cScene>();
//CGTaskHandler1<cScene> alomelo = LoadScene;
//back.RegisterCGTask(alomelo, thisScene);
return back;
}
so I changed my cgtask class to a generic class, so, argument type would be definite since when class is instantiated.
public class CGTask2<T>
{
private CGTaskHandler1<T> Aksiyon;
private T param;
public void RegisterCGTask(CGTaskHandler1<T> aFunc, T aParam)
{
Aksiyon = aFunc;
param = aParam;
}
public void ExecuteCGTask()
{
try
{
Aksiyon(param);
}
catch (Exception ex)
{
Logger.SetLog("action execution failed ", LogType.error, ex.Message);
}
}
}
however same problem I confront when I want to collect them in a list.
List<CGTask2<IControllerBase>> gorevler = new List<CGTask2<IControllerBase>>();
gorevler.Add(new CGTask2<cScene>()); // type mismatch
I need a way to keep functions like objects. Every time I make use of a generic function delegate I need to specify the type, and types of generic functions are not convertible. Is there a way to do this, keeping references to functions and collecting these references as objects?
public interface IControllerBase
{
void GetTalker();
void InitiliazeTalker();
}
public class cControllerBase : IControllerBase
{
public cControllerBase Parent=null;
protected Talker tk;
protected void GetTalker()
{
tk = Talker.Instance; // not initialized yet
}
protected void InitiliazeTalker()
{
tk.InitializeReTalk();
}
}
public class cScene : cControllerBase, IControllerBase
{
public String ID;
public String ScenePath;
public String SceneName;
public int Slot;
public String DBParent;
public List<cAnimation> Animations;
public List<cExport> Exports;
public Boolean IsActive;
public cScene()
{
GetTalker();
Animations = new List<cAnimation>();
Exports = new List<cExport>();
// ID = Guid.NewGuid().ToString();
IsActive = false;
}
public Boolean ParseXml(String pXmlPath)
{
if (String.IsNullOrEmpty(pXmlPath)) return false;
XmlDocument xdoc = new XmlDocument();
XmlNodeList anims = null;
XmlNodeList exps = null;
try
{
xdoc.Load(pXmlPath);
anims = xdoc.SelectNodes("//scene_description/animations/animation");
exps = xdoc.SelectNodes("//scene_description/exports/export");
}
catch (Exception ex)
{
Logger.SetLog("xml parse error", LogType.error, ex.Message);
return false;
}
cAnimation tempanim;
cExport tempexport;
foreach (XmlNode x in anims)
{
tempanim = new cAnimation();
foreach (XmlAttribute y in x.Attributes)
{
switch (y.Name)
{
case "name":
{
tempanim.AnimationName = y.Value;
break;
}
case "duration":
{
tempanim.AnimationDuration = Globals.GetIntValue(y.Value);
break;
}
case "end_animation_time":
{
tempanim.AnimationEndTime = Globals.GetIntValue(y.Value);
break;
}
case "start_animation_time":
{
tempanim.AnimationStartTime = Globals.GetIntValue(y.Value);
break;
}
}
}
tempanim.Parent = this;
Animations.Add(tempanim);
}
foreach (XmlNode x in exps)
{
tempexport = new cExport();
foreach (XmlAttribute y in x.Attributes)
{
switch (y.Name)
{
case "name":
{
tempexport.ExportName = y.Value;
break;
}
case "type":
{
switch (y.Value)
{
case "String":
{
tempexport.ExportType = ExportDataType.tString;
break;
}
case "File":
{
tempexport.ExportType = ExportDataType.tFile;
break;
}
case "Float":
{
tempexport.ExportType = ExportDataType.tFloat;
break;
}
case "Int":
{
tempexport.ExportType = ExportDataType.tInt;
break;
}
case "Bool":
{
tempexport.ExportType = ExportDataType.tBool;
break;
}
}
break;
}
case "value":
{
tempexport.ExportValue = y.Value;
break;
}
}
}
tempexport.Parent = this;
Exports.Add(tempexport);
}
return true;
}
public void ActivateScene()
{
tk.ActivateScene(Slot, SceneName);
IsActive = true;
}
public void DeactivateScene()
{
// to do
// tk'dan aktif scene listesi yapıp kontrol edebiliyor musun?
tk.DeactivateScene(Slot);
IsActive = false;
}
public Boolean IsSceneLoaded()
{
Boolean back = false;
back = tk.IsSceneLoaded(SceneName);
return back;
}
public void LoadScene()
{
tk.LoadScene(SceneName);
}
public void UnloadScene()
{
tk.UnloadScene(SceneName);
}
public void SetSceneName(String strxmlPath)
{
ScenePath = strxmlPath;
SceneName = strxmlPath.Substring(0, strxmlPath.LastIndexOf('\\'));
SceneName = SceneName.Replace('\\', '/');
SceneName = SceneName.Substring(SceneName.IndexOf("Projects") + 9);
}
}
Well a CGTask2<cScene> is a completely different type to CGTask2<IControllerBase>, you can't equate the two. You would have to have a list of, for example, ITask and make CGTask2 implement that. For example:
public interface ITask {}
public class CGTask2<T> : ITask
{
//snip
}
And now you can do this:
List<ITask> gorevler = new List<ITask>();
gorevler.Add(new CGTask2<cScene>());

data structure of objects and methods in c#

I am new to c# and fairly new to programming. I need help with a topic which i have been trying to figure out from the past week. I have 3 files:
Control: this is an interface and should contain the list of my
methods
ControlImpl : this the implementaion of the interfaces.
Runtime: contains which the binding code between the main method
and the interface implementaion
Test_main: from where i call the
runtime method 'call'
Problem: there can be any number of instances(for ex: c, c1, c2, etc) in Control file and each instance should be able to call SetTime() and Nop() methods.
I made a list of the methods SetTime() and Nop(). But how can i add the instance to a list so that each instance when called should call its methods?
CONTROL
namespace create_interface
{
interface Control
{
void SetTime(params object[] paramsArr);
void Nop(params object[] paramsArr);
}
public class CM
{
Control c = new ControlImpl();
public List<object> ControlMain()
{
List<object> methods = new List<object>();
methods.Add(new Action<object[]>(c.SetTime));
methods.Add(new Action<object[]>(c.Nop));
return methods;
}
}
}
ControlImpl :
namespace create_interface
{
public class ControlImpl : Control
{
void Control.SetTime(params object[] paramsArr)
{
Console.WriteLine("inside Control.SetTime {0} ", paramsArr[0]);
}
void Control.Nop(params object[] paramsArr)
{
Console.WriteLine("inside Control.Nop ");
}
}
}
Runtime:
namespace create_interface
{
public class runtime
{
public void call(params object[] methodparams)
{
if ((methodparams[0].Equals(0)) || (methodparams[0].Equals(1)))
{
//List<Control> objectlists = cmObject.ControlObjectList();
List<object> methods = cmObject.ControlMain();
//Console.WriteLine(methods.Count);
Action<object[]> method = (Action<object[]>)methods[(int)methodparams[0]]; //object[]
object[] args = new object[] { methodparams[1] };
method(args);
}
else
Console.WriteLine("wrong ID number entered");
}
Test_main:
namespace create_interface
{
class test_main
{
static void Main(string[] args)
{
long time;
CallingFunc CF = new CallingFunc();
Console.WriteLine("enter method ID");
int methodID = Convert.ToInt32(Console.ReadLine());
try
{
switch (methodID)
{
case 0:
Console.WriteLine("enter the time in long");
time = Convert.ToInt64(Console.ReadLine());
CF.call(methodID, time);
break;
case 1:
CF.call(methodID, null);
break;
default:
Console.WriteLine("you entered wrong method ID or parameters");
break;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Please take a look at the following solution and we can use it as a base to come up with your final solution:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace StackOverflow38200633
{
class Program
{
static void Main(string[] args)
{
Collection<IControl> controls = new Collection<IControl>();
controls.Add(ControlFactory.Create());
controls.Add(ControlFactory.Create());
controls.Add(ControlFactory.Create());
ControlManager manager = new ControlManager(controls);
Console.WriteLine("Enter method ID:");
int methodID = Convert.ToInt32(Console.ReadLine());
try
{
switch(methodID)
{
case 0:
Console.WriteLine("Enter the time in long: ");
long time = Convert.ToInt64(Console.ReadLine());
manager.InvokeAllSetTime(time);
break;
case 1:
manager.InvokeAllNop();
break;
default:
Console.WriteLine("You entered wrong method ID or parameters");
break;
}
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
public interface IControl
{
void SetTime(long time);
void Nop();
}
public class ConcreteControl : IControl
{
public void SetTime(long time)
{
Console.WriteLine("inside Control.SetTime {0} ", time);
}
public void Nop()
{
Console.WriteLine("inside Control.Nop ");
}
}
public class ControlManager
{
public void InvokeAllSetTime(long time)
{
foreach(IControl control in _controls) control.SetTime(time);
}
public void InvokeAllNop()
{
foreach(IControl control in _controls) control.Nop();
}
public ControlManager(Collection<IControl> controls)
{
_controls = controls;
}
public Collection<IControl> _controls { get; private set; }
}
public static class ControlFactory
{
public static IControl Create()
{
return new ConcreteControl();
}
}
}

Categories