How do I bind MouseDoubleClick event of wpfdatagrid in the view as I'm using mvvm and Prism 2.
I prefer adding a MouseDoubleClickBehaviour and then you can attach it to any control, which will bind to your ViewModel. Calling commands from the View's code-behind creates direct dependencies which I don't like.
public static class MouseDoubleClickBehaviour
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(MouseDoubleClickBehaviour), new UIPropertyMetadata(null, OnCommandChanged));
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(MouseDoubleClickBehaviour), new UIPropertyMetadata(null));
public static ICommand GetCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandProperty, value);
}
public static object GetCommandParameter(DependencyObject obj)
{
return obj.GetValue(CommandParameterProperty);
}
public static void SetCommandParameter(DependencyObject obj, object value)
{
obj.SetValue(CommandParameterProperty, value);
}
private static void OnCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
var grid = target as Selector;
////Selector selector = target as Selector;
if (grid == null)
{
return;
}
grid.MouseDoubleClick += (a, b) => GetCommand(grid).Execute(grid.SelectedItem);
}
}
Then you can do this in your XAML
<ListView ...
behaviours:MouseDoubleClickBehaviour.Command="{Binding Path=ItemSelectedCommand}"
behaviours:MouseDoubleClickBehaviour.CommandParameter="{Binding ElementName=txtValue, Path=Text}"
.../>
Listen to the MouseDoubleClick event in the code-behind of the View and call the appropriate method on the ViewModel:
public class MyView : UserControl
{
...
private MyViewModel ViewModel { get { return DataContext as MyViewModel; } }
private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ViewModel.OpenSelectedItem();
}
Related
I am learning Xamarin forms.
I wanted to be able to bind some ViewModel to some values in a DataManager class (Singleton).
Say the Singleton is a BleManager and all ViewModel need to use it to get or set some information to or from the BLE device.
I Know how to bind my VM to the XAML view code.
But now I need to be able to get the viewModels to update local Data when the BLEManager updates some info, like battery level.
so for example (semi sudo-code).
public class BLEInterface: INotifyPropertyChanged
{
protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public async Task<float> GetDeviceName()
{
await Task.Delay(1000);
return float.random(0f:100f)
}
}
public sealed class BLEManager: INotifyPropertyChanged
{
private static BLEManager shared;
private static object objectLockCheck = new Object();
private BLEInterface BleModel { get; set; }
private float batteryLevel;
public float BatteryLevel {
get => batteryLevel;
set {
batteryLevel = value;
OnNotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnNotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private BLEManager()
{
}
public async Task ConnectToBLE()
{
await Task.Delay(1000);
BleModel = new TestModel();
}
public async void GetBatteryLevel()
{
BatteryLevel = await bleModel.GetDeviceName();
}
public static BLEManager Shared
{
get
{
if(shared == null)
{
lock (objectLockCheck)
{
if(shared == null)
{
shared = new BLEManager();
}
}
}
return shared;
}
}
}
The Part I need to know is how can my viewModel hook on changes from the BleManager battery level property.
I'm trying to configure IoC (concept I'm not very familiar with yet) in my SF in a stateful service as explained here : https://www.codeproject.com/Articles/1217885/Azure-Service-Fabric-demo and here : https://alexmg.com/posts/introducing-the-autofac-integration-for-service-fabric.
in program.cs - main:
var builder = new ContainerBuilder();
builder.RegisterModule(new GlobalAutofacModule());
builder.RegisterServiceFabricSupport();
builder.RegisterStatefulService<Payment>("PaymentType");
using (builder.Build())
{
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Payment).Name);
Thread.Sleep(Timeout.Infinite);
}
GlobalAutofacModule :
public class GlobalAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ChargeRepository>().As<IChargeRepository>().SingleInstance();
builder.RegisterType<CustomerRepository>().As<ICustomerRepository>().SingleInstance();
builder.RegisterType<InvoiceItemRepository>().As<IInvoiceItemRepository>().SingleInstance();
builder.RegisterType<PlanRepository>().As<IPlanRepository>().SingleInstance();
builder.RegisterType<ProductRepository>().As<IProductRepository>().SingleInstance();
builder.RegisterType<SourceRepository>().As<ISourceRepository>().SingleInstance();
builder.RegisterType<SubscriptionRepository>().As<ISubscriptionRepository>().SingleInstance();
builder.RegisterType<TokenRepository>().As<ITokenRepository>().SingleInstance();
}
}
the service is called without problems
public Payment(StatefulServiceContext context,
IChargeRepository chargeRepo,
ICustomerRepository customerRepo,
IInvoiceItemRepository invoiceItemRepo,
IPlanRepository planRepository,
IProductRepository productRepo,
ISourceRepository sourceRepo,
ISubscriptionRepository subscriptionRepo,
ITokenRepository tokenRepo)
: base(context)
{ ... }
in one of it's methodes it needs to call a custom mapper (error on missing params)
var test = new Mapper().GetProductsDto(false, false);
the class is defined like this :
private readonly IChargeRepository _chargeRepo;
private readonly ICustomerRepository _customerRepo;
private readonly IInvoiceItemRepository _invoiceItemRepo;
private readonly IPlanRepository _planRepo;
private readonly IProductRepository _productRepo;
private readonly ISourceRepository _sourceRepo;
private readonly ISubscriptionRepository _subscriptionRepo;
private readonly ITokenRepository _tokenRepo;
public Mapper(IChargeRepository chargeRepo,
ICustomerRepository customerRepo,
IInvoiceItemRepository invoiceItemRepo,
IPlanRepository planRepository,
IProductRepository productRepo,
ISourceRepository sourceRepo,
ISubscriptionRepository subscriptionRepo,
ITokenRepository tokenRepo)
{
_chargeRepo = chargeRepo;
_customerRepo = customerRepo;
_invoiceItemRepo = invoiceItemRepo;
_planRepo = planRepository;
_productRepo = productRepo;
_sourceRepo = sourceRepo;
_subscriptionRepo = subscriptionRepo;
_tokenRepo = tokenRepo;
}
public IEnumerable<ProductListDto> GetStripeProductsDto(bool isLogged, bool isSubscriber) {...}
So how do I instantiate the mapper and call the method without passing every repo as params ?
EDIT: tmp solution until approuved/disapprouved
private static void Main()
{
try
{
using (ContainerOperations.Container)
{
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Payment).Name);
Thread.Sleep(Timeout.Infinite);
}
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
}
public class ContainerOperations
{
private static readonly Lazy<IContainer> _containerSingleton =
new Lazy<IContainer>(CreateContainer);
public static IContainer Container => _containerSingleton.Value;
private static IContainer CreateContainer()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new GlobalAutofacModule());
builder.RegisterServiceFabricSupport();
builder.RegisterStatefulService<Payment>("Inovatic.SF.Windows.PaymentType");
return builder.Build();
}
}
public class GlobalAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//builder.RegisterType<Mapper>();
builder.RegisterType<ChargeRepository>().As<IChargeRepository>().SingleInstance();
builder.RegisterType<CustomerRepository>().As<ICustomerRepository>().SingleInstance();
builder.RegisterType<InvoiceItemRepository>().As<IInvoiceItemRepository>().SingleInstance();
builder.RegisterType<PlanRepository>().As<IPlanRepository>().SingleInstance();
builder.RegisterType<ProductRepository>().As<IProductRepository>().SingleInstance();
builder.RegisterType<SourceRepository>().As<ISourceRepository>().SingleInstance();
builder.RegisterType<SubscriptionRepository>().As<ISubscriptionRepository>().SingleInstance();
builder.RegisterType<TokenRepository>().As<ITokenRepository>().SingleInstance();
}
}
call is now like this : var productListDto = Mapper.GetStripeProductsDto(isLogged, false);
mapper:
private static IProductRepository _productRepo => ContainerOperations.Container.Resolve<IProductRepository>();
public static IEnumerable<ProductListDto> GetStripeProductsDto(bool isLogged, bool isSubscriber)
{
var productList = _productRepo.GetAllStripeProducts().ToList();
I think you should also register Mapper class in IoC container and add it to Payment's constructor, then container will create Mapper with all required params for you. You can do it calling something like
builder.RegisterType<Mapper>().SingleInstance();
I have a Main View with a toolbar and a TabControl region that has two views registered: View A, View B. All views should have as DataContext the same instance of ContactsViewModel, but in fact, each view is creating a new instance of ContactsViewModel.
This is the Main view code-behind:
public partial class ContactsView : UserControl
{
public IRegionManager regionManager;
private static Uri listViewUri = new Uri("/ContactsListView", UriKind.Relative);
private static Uri tilesViewUri = new Uri("/ContactsTilesView", UriKind.Relative);
public ContactsView(ContactsViewModel contactsViewModel, IRegionManager regionManager, IUnityContainer container)
{
this.ViewModel = contactsViewModel;
container.RegisterType<ContactsViewModel>();
this.regionManager = regionManager;
InitializeComponent();
}
public ContactsViewModel ViewModel
{
get { return this.DataContext as ContactsViewModel; }
set { this.DataContext = value; }
}
}
This is the view A code-behind:
public partial class ContactsListView : UserControl
{
public ContactsListView(IUnityContainer container)
{
ContactsViewModel viewModel = container.Resolve<ContactsViewModel>();
this.ViewModel = viewModel;
InitializeComponent();
SetupColumns();
}
public ContactsViewModel ViewModel
{
get { return this.DataContext as ContactsViewModel; }
set { this.DataContext = value; }
}
}
View B is similar to View A.
And this is the ViewModel:
public class ContactsViewModel : BindableBase
{
private readonly IRegionManager regionManager;
private readonly IEventAggregator eventAggregator;
private readonly IConfigurationContactsService contactsService;
private readonly DelegateCommand<object> deleteContactCommand;
private ObservableCollection<Contact> contactsCollection;
private ICollectionView contactsView;
public ContactsViewModel(IEventAggregator eventAggregator, IConfigurationContactsService contactsService, IRegionManager regionManager)
{
this.regionManager = regionManager;
this.contactsService = contactsService;
this.eventAggregator = eventAggregator;
this.deleteContactCommand = new DelegateCommand<object>(this.DeleteContact, this.CanDeleteContact);
this.contactsCollection = new ObservableCollection<Contact>(contactsService.GetContacts());
this.contactsView = CollectionViewSource.GetDefaultView(this.contactsCollection);
}
public ICollectionView ContactsView
{
get { return this.contactsView; }
}
public ObservableCollection<Contact> Contacts
{
get { return this.contactsCollection; }
}
public ICommand DeleteContactCommand
{
get { return this.deleteContactCommand; }
}
private void DeleteContact(object ignore)
{
IList<Contact> selectedContacts = contactsService.GetSelectedContacts();
foreach (Contact contact in selectedContacts)
{
if (contact != null)
{
contactsService.DeleteContact(contact);
}
}
SetProperty<ObservableCollection<Contact>>(ref this.contactsCollection, new ObservableCollection<Contact>(contactsService.GetContacts()), "Contacts");
}
private bool CanDeleteContact(object ignored)
{
return contactsService.GetSelectedContacts().Any();
}
}
How can I do ContactsListView (here called View A) to have the same instance of ContactsViewModel than the MainView?
EDITTED
Code in Main View and View A editted so in Main View I register the ViewModel into the container and in View A I Resolve the viewmodel. Still getting three instances. When the view model is resolved, a new instance is created.
As Richards suggested, I fixed the issue by registering the viewmodel as a singleton:
container.RegisterInstance<ContactsViewModel>(contactsViewModel);
I've been at it for days, but I can't get Unity to inject anything with RegisterType<> into my Controller. I'm using Web Api 2, in Visual Studio 2015, with Unity 4. Whenever I try to inject IUnitOfWork or IRFContext, I get "message": "An error occurred when trying to create a controller of type 'ClPlayersController'. Make sure that the controller has a parameterless public constructor.".
I'm using the Unity.AspNet.WebApi to bootstrapp into WebApi. Below is my UnityWebApiActivator
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(mycompany.project.api.UnityWebApiActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(mycompany.project.api.UnityWebApiActivator), "Shutdown")]
namespace mycompany.project.api
{
public static class UnityWebApiActivator
{
public static void Start()
{
var resolver = new UnityDependencyResolver(UnityConfig.GetConfiguredContainer());
GlobalConfiguration.Configuration.DependencyResolver = resolver;
}
public static void Shutdown()
{
var container = UnityConfig.GetConfiguredContainer();
container.Dispose();
}
}
}
I'm using a Start.cs due to Owin.
[assembly: OwinStartup(typeof(mycompany.project.api.Startup))]
namespace mycompany.project.api
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
config.DependencyResolver = new UnityDependencyResolver(UnityConfig.GetConfiguredContainer());
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}
My WebApiConfig.cs is below:
namespace mycompany.project.api
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
log4net.Config.XmlConfigurator.Configure();
config.MapHttpAttributeRoutes();
config.EnableSystemDiagnosticsTracing();
config.Services.Add(typeof(IExceptionLogger),
new SimpleExceptionLogger(new LogManagerAdapter()));
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
}
}
}
My UnityConfig.cs is below
namespace mycompany.project.api
{
public class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
#endregion
public static void RegisterTypes(IUnityContainer container)
{
var config = new MapperConfiguration(cfg =>
{
//AutoMapper bindings
});
container.RegisterInstance<IMapper>(config.CreateMapper());
container.RegisterType<IRFContext, RFContext>(new PerThreadLifetimeManager());
container.RegisterType<IUnitOfWork, UnitOfWork>();
XmlConfigurator.Configure();
var logManager = new LogManagerAdapter();
container.RegisterInstance<ILogManager>(logManager);
}
}
}
All that I have in my Global.asax is below:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Error()
{
var exception = Server.GetLastError();
if (exception != null)
{
var log = new LogManagerAdapter().GetLog(typeof(WebApiApplication));
log.Error("Unhandled exception.", exception);
}
}
}
If my Controller is like this, it works fine:
public class ClPlayersController : ApiController
{
private readonly IMapper mapper;
public ClPlayersController(IMapper _mapper, IUnityContainer container)
{
mapper = _mapper;
}
But placing IUnitOfWork, like below, or the IRFContext, I get the error:
private readonly IMapper mapper;
private readonly IUnitOfWork unitOfWork;
public ClPlayersController(IMapper _mapper, IUnityContainer container, IUnitOfWork _unitOfWork)
{
mapper = _mapper;
unitOfWork = _unitOfWork;
}
I can't find, for the life of me, what I'm doing wrong. If I loop through the container.Registrations on the constructor, I find the mappings, but they refuse to get injected. Any hints?
EDIT
Below is the code for UnitOfWork and RFContext
namespace mycompany.project.data.configuracao
{
public class UnitOfWork : IUnitOfWork
{
private readonly IRFContext _rfContext;
private bool _disposed = false;
public UnitOfWork(IRFContext rfContext)
{
_rfContext = rfContext;
}
public void Commit()
{
if (_disposed)
{
throw new ObjectDisposedException(this.GetType().FullName);
}
_rfContext.SaveChanges();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing && _rfContext != null)
{
_rfContext.Dispose();
}
_disposed = true;
}
}
}
and
namespace mycompany.project.data.configuracao
{
public interface IUnitOfWork : IDisposable
{
void Commit();
}
}
and RFContext is a basic POCO generated DBContext
namespace mycompany.project.data.configuracao
{
using System.Linq;
public class RFContext : System.Data.Entity.DbContext, IRFContext
{
public System.Data.Entity.DbSet<ClGrupoEconomico> ClGrupoEconomicoes { get; set; }
//all my DbSets
public System.Data.Entity.DbSet<SpTipoLog> SpTipoLogs { get; set; }
static RFContext()
{
System.Data.Entity.Database.SetInitializer<RFContext>(null);
}
public RFContext()
: base("Name=RFContext")
{
}
public RFContext(string connectionString)
: base(connectionString)
{
}
public RFContext(string connectionString, System.Data.Entity.Infrastructure.DbCompiledModel model)
: base(connectionString, model)
{
}
public RFContext(System.Data.Common.DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
}
public RFContext(System.Data.Common.DbConnection existingConnection, System.Data.Entity.Infrastructure.DbCompiledModel model, bool contextOwnsConnection)
: base(existingConnection, model, contextOwnsConnection)
{
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ClGrupoEconomicoConfiguration());
//all my Configuration classes
modelBuilder.Configurations.Add(new SpTipoLogConfiguration());
}
public static System.Data.Entity.DbModelBuilder CreateModel(System.Data.Entity.DbModelBuilder modelBuilder, string schema)
{
modelBuilder.Configurations.Add(new ClGrupoEconomicoConfiguration(schema));
//all my configuration classes
modelBuilder.Configurations.Add(new SpTipoLogConfiguration(schema));
return modelBuilder;
}
}
}
Unfortunately the exception you are seeing can occur for several reasons. One of them is when Unity cannot resolve one or more of your injections.
An error occurred when trying to create a controller of type
'FooController'. Make sure that the controller has a parameterless
public constructor.
So, based on the information in your question your setup is apparently correct, since IMapper can be injected. Therefore I guess that UnitOfWork and RFContext have dependencies that Unity cannot resolve. Maybe a repository?
UPDATE:
The problem here is that your RFContext has several constructors.
https://msdn.microsoft.com/en-us/library/cc440940.aspx#cnstrctinj_multiple
When a target class contains more than one constructor with the same
number of parameters, you must apply the InjectionConstructor
attribute to the constructor that the Unity container will use to
indicate which constructor the container should use. As with automatic
constructor injection, you can specify the constructor parameters as a
concrete type, or you can specify an interface or base class for which
the Unity container contains a registered mapping.
In this case Unity doesn't know how to resolve your RFContext, and will try to use the constructor with the most parameters. You can solve it by using
container.RegisterType<IRFContext, RFContext>(new InjectionConstructor());
I am having a model not in EF, but in plain text. I have to have the updated events handled for each of the model's properties so that i can log their changes.
Is there a way for this to be achieved.
Implement the INotifyPropertyChanged interface.
A simple example:
using System.ComponentModel;
public class MyModel : INotifyPropertyChanged
{
string _myProperty;
public event PropertyChangedEventHandler PropertyChanged;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
NotifyPropertyChanged("MyProperty");
}
}
public void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
You can use it like...
public class Test
{
public static void Main()
{
var model = new MyModel();
model.PropertyChanged += new PropertyChangedEventHandler(LogChange);
model.MyProperty="apples";
model.MyProperty="oranges";
model.MyProperty="pears";
}
public static void LogChange(object sender, PropertyChangedEventArgs args)
{
Console.WriteLine(args.PropertyName + " has changed!");
Console.WriteLine("New value: "
+ sender.GetType().GetProperty(args.PropertyName)
.GetValue(sender, null));
}
}