Win8 Share Contract with Portable Class Library - mvvm

I am trying to receive shared text in a windows 8 store application via the share contract. I can receive the text, but if the text makes its way into an observable collection I get a com exception. How do I receive shared text and pass it off correctly to a viewmodel in a portable class library?
Portable class library:
ViewModel.cs:
public class ViewModel
{
public ObservableCollection<string> Strings { get; private set; }
public ViewModel()
{
Strings = new ObservableCollection<string>
{
"one",
"two",
"three"
};
}
}
Locator.cs:
public class Locator
{
private static ViewModel vm;
public static ViewModel VM
{
get
{
if (vm == null)
{
vm = new ViewModel();
}
return vm;
}
}
}
Store App Project:
MainPage.xaml
<Page...>
<Page.Resources>
<vm:Locator x:Key="Locator"/>
</Page.Resources>
<Grid ... DataContext="{Binding VM, Source={StaticResource Locator}}">
<ListBox ItemsSource="{Binding Strings}" .../>
</Grid>
</Page>
App.xaml.cs (abbreviated):
protected override async void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
base.OnShareTargetActivated(args);
if (args.ShareOperation.Data.AvailableFormats.Contains("Text"))
{
var text = await args.ShareOperation.Data.GetTextAsync();
Locator.VM.Strings.Add(text);
}
}
The last line above ( Locator.VM.Strings.Add(text) ) throws the following exception:
An unhandled exception of type 'System.InvalidCastException' occurred
in mscorlib.dll
Additional information: Unable to cast COM object of type
'System.Collections.Specialized.NotifyCollectionChangedEventHandler'
to class type
'System.Collections.Specialized.NotifyCollectionChangedEventHandler'.
Instances of types that represent COM components cannot be cast to
types that do not represent COM components; however they can be cast
to interfaces as long as the underlying COM component supports
QueryInterface calls for the IID of the interface.

Related

need help on MVVM structure

I am new to Xarmain Forms programming and try to follow the MVVM model. However, I am not sure if I am doing it correctly. The program I write works, but
I would like some experts' opinions on whether I am doing it right or not in terms of MVVM.
I see a lot of example having the OnPropertyChanged somewhere in the programs. Do I need it somewhere in my program?
Any way to simplify?
My program read the text file in the system pathe and the text file contains some URLs and my XAML will display what the URL points to.
+++++++++++++++++++++++++++++++++++++++++++++++++
Folder name: Model
Class name: ListViewNewsItem.cs
namespace SCAC.Models
{
public class ListViewNewsItem
{
public string NewsLink { get; internal set; }
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++
Folder Name: ViewModels
Class Name: ListViewNewsViewModel.cs
namespace SCAC.ViewModels
{
public class ListViewNewsViewModel : ViewModel
{
private ObservableCollection<ListViewNewsItem> newsItem;
private static string documentPath = ReturnDocumentPath(); // Get the system path
public ListViewNewsViewModel()
{
GenerateNews(); // code below
}
public ObservableCollection<ListViewNewsItem> NewsItem
{
get { return newsItem; }
set
{
this.newsItem = value;
}
}
public void GenerateNews()
{
// File
string line;
string toReadFile = Path.Combine(documentPath + "Images.txt");
NewsItem = new ObservableCollection<ListViewNewsItem>();
StreamReader toRead = new StreamReader(toReadFile);
while ((line = toRead.ReadLine()) != null)
{
Console.WriteLine("This is the url: " + line);
var newsDetail = new ListViewNewsItem()
{
NewsLink = line
};
NewsItem.Add(newsDetail);
}
toRead.Close();
}
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Folder Name: Views
XAML file Name: NewsView.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:localvm="clr-namespace:SCAC.ViewModels"
mc:Ignorable="d"
x:Class="SCAC.Views.NewsView" >
<!-- ontentPage.BindingContext>
<localvm:ListViewNewsViewModel />
</ -->
<ContentPage.Content>
<ScrollView>
<StackLayout BindableLayout.ItemsSource="{Binding NewsItem}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Frame Padding="10, 10, 10, 10" HeightRequest="200">
<Image Source="{Binding ImageLink}" Aspect="Fill"></Image>
</Frame>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Code behind: NewsView.xaml.cs
namespace SCAC.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NewsView : ContentPage
{
public NewsView()
{
InitializeComponent();
BindingContext = new ListViewNewsViewModel();
}
}
}
I think you did a quite good job in MVVM structure. Get the data in the ViewModel, set the binding in the Xaml, set the right bindingContext to contect View and ViewModel. Everything looks well so far.
The Model-View-ViewModel (MVVM) architectural pattern was invented
with XAML in mind. The pattern enforces a separation between three
software layers — the XAML user interface, called the View; the
underlying data, called the Model; and an intermediary between the
View and the Model, called the ViewModel. The View and the ViewModel
are often connected through data bindings defined in the XAML file.
The BindingContext for the View is usually an instance of the
ViewModel.
ViewModels generally implement the INotifyPropertyChanged interface, which means that the class fires a PropertyChanged event whenever one of its properties changes.
You does not need to implement the INotifyPropertyChanged interface because there is only one property in your ViewModel: ObservableCollection<ListViewNewsItem> NewsItem.
ObservableCollection implements the INotifyCollectionChanged interface by default so you don't have to implement the INotifyPropertyChanged interface in ViewModel to notify the data changes.
If you have a simple property like public DateTime DateTime, then you usually need to write it like:
class ClockViewModel : INotifyPropertyChanged
{
DateTime dateTime;
public event PropertyChangedEventHandler PropertyChanged;
public DateTime DateTime
{
set
{
if (dateTime != value)
{
dateTime = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("DateTime"));
}
}
}
get
{
return dateTime;
}
}
}
You can read the document to learn more about MVVM with data-binding.

Xamarin Forms MVVM Databinding failing when I'm binding to a single object

I'm having an issue with data not binding correctly on a details page when I have clicked through from a ListView via a button. The ListView binds perfectly and the object gets passed through to the details page. The Id of the object is read and a full version of the object is called from an API and set to a new instance of the object. When I add a breakpoint, the full object is available, but Labels on the view aren't populated. Here is the ViewModel:
DetailsViewModel.cs
public class DetailsViewModel
{
public Deal Deal { get; set; }
public int DealId { get; set; }
public DetailsViewModel(int id)
{
Deal = new Deal();
DealId = id;
}
public async void GetDeal()
{
var deal = await Deal.GetDeal(DealId);
if(deal != null)
{
Deal = deal;
}
}
}
The codebehind looks like this:
DetailPage.Xaml.cs
DetailsViewModel viewModel;
int dealId;
public DetailPage(int id)
{
InitializeComponent();
dealId = id;
viewModel = new DetailsViewModel(dealId);
BindingContext = viewModel;
}
protected override void OnAppearing()
{
base.OnAppearing();
viewModel.GetDeal();
}
And the Xaml file is
DetailPage.Xaml
<ContentPage.Content>
<ScrollView>
<StackLayout x:Name="detailsLayout">
<Label Text="{Binding Deal.Name}" />
</StackLayout>
</ScrollView>
</ContentPage.Content>
When I put a breakpoint in Deal = deal on DetailsViewModel, the Deal object exists and has the correct data, but I just get a blank screen. I have tried Labels with Text="{Binding Name}" and Text="{Binding Deal.Name}".
I have also tried manually creating a deal in the GetDeal function of the ViewModel and still nothing is bound.
1) Ensure your property Notifies the UI of a change implementing the INotifyPropertyChanged interface. See https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm
2) Ensure the set is done on the UI thread using Device.BeginInvokeOnMainThread. https://learn.microsoft.com/fr-fr/dotnet/api/xamarin.forms.device.begininvokeonmainthread?view=xamarin-forms
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace YourNamespace
{
public class DetailsViewModel : INotifyPropertyChanged
{
private Deal _deal;
public Deal Deal
{
get => _deal;
set
{
if (_deal != value)
{
_deal = value;
OnPropertyChanged();
}
}
}
public int DealId { get; set; }
public DetailsViewModel(int id)
{
//!! useless assignation
//Deal = new Deal();
DealId = id;
}
public async void GetDeal()
{
var deal = await Deal.GetDeal(DealId);
if (deal != null)
{
//Ensure we are on UI thread
Device.BeginInvokeOnMainThread(() => Deal = deal);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Dependency property inside viewmodel in Prism

Is there any way to declare dependency property inside viewmodel? I want to declare a dependency property inside viewmodel and change it's value through command.
public class MyViewModel : Prism.Windows.Mvvm.ViewModelBase
{
public bool IsPaneVisible
{
get { return (bool)GetValue(IsPaneVisibleProperty); }
set { SetValue(IsPaneVisibleProperty, value); }
}
public static readonly DependencyProperty IsPaneVisibleProperty =
DependencyProperty.Register("IsPaneVisible", typeof(bool), typeof(MyViewModel), new PropertyMetadata(0));
public ICommand VisibilityChangeCommand { get; set; }
public MyViewModel()
{
VisibilityChangeCommand = new DelegateCommand(OnVisibilityChange);
}
private void OnVisibilityChange()
{
IsPaneVisible = !IsPaneVisible;
}
}
Problem is, I am getting some compilation error in IsPaneVisible' getter/setter : "GetValue does not exist in the current context". Is there any alternative way to do this?
A DependencyProperty is used on a DependencyObject, an example of this is a UserControl. Prism's ViewModelBase is no DependencyObject, mainly because this type is platform specific. To support binding from a viewmodel, we typically use INotifyPropertyChanged.
Prism implements this interface in the BindableBase base class, from which ViewModelBase derives as well. You define your properties like this:
private string _imagePath;
public string ImagePath
{
get { return _imagePath; }
set { SetProperty(ref _imagePath, value); }
}
If you install the Prism Template Pack Visual Studio extension, you can use the propp code snippet.

How to redirect ILoggerFacade to ViewModel?

I want write my logs to the ViewModel, so I can expose logs to the users.
First I bind View to ViewModel
<TextBox Grid.Row="1" TextWrapping="Wrap" Text="{Binding Logger}" AcceptsReturn="True" IsReadOnly="True"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
This is the ViewModel
private string logger;
public string Logger
{
get { return logger; }
set
{
logger = value;
this.RaisePropertyChanged("Logger");
}
}
Then, I create the customer logger class which implements ILoggerFacade,and override the CreateLogger method in Bootstrapper.
In Bootstrapper
protected override ILoggerFacade CreateLogger()
{
return new MainLogger();
}
In Customer Logger class
public class MainLogger : ILoggerFacade
{
public void Log(string message, Category category, Priority priority)
{
string messageToLog = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{1}: {2}. Priority: {3}. Timestamp:{0:u}.", DateTime.Now, category.ToString().ToUpper(System.Globalization.CultureInfo.InvariantCulture), message, priority.ToString());
//////??????//////
}
}
And what should fill in ???????. I tried Import IEventAggregator to publish data to ViewModel, and directly import ViewModel here. Neither works, because CreatorLogger Method is called before container is registered. So how can I write logs to the ViewModel?
The logger should simply save the log message(s) and expose it in a property:
public interface IMainLogger : ILoggerFacade
{
List Messages { get; }
}
public class MainLogger : IMainLogger
{
public MainLogger()
{
Messages = new ObservableCollection<string>();
}
public ObservableCollection<string> Messages { get; private set; }
public void Log(string message, Category category, Priority priority)
{
string messageToLog = ...;
Messages.Add(messageToLog);
}
}
That's basically what a logger is supposed to do: Log messages. Now you want to display it inside some TextBox which is contained in a View that you inject into a region, right? To do that, you need to pass that logger to the Module of this region's view via constructor dependency injection. I am working with MEF, so I'm not too sure about how to do it with Unity, but it probably looks something like this, when you configure the container in code:
container.RegisterType<IMainLogger, MainLogger>(new ContainerControlledLifetimeManager());
container.RegisterType<ContainingTextBoxModule>(new InjectionConstructor(container.Resolve<IMainLogger>()));
where the module takes and exposes the logger:
public class ContainingTextBoxModule : IModule
{
public IMainLogger Logger { get; private set; }
public ContainingTextBoxModule(IMainLogger logger)
{
Logger = logger;
}
}
Then your ViewModel for the module can relay the message(s) and you can bind to it/them from your view.
Does this answer your question?

Log method parameters and return type using Enterprise library logging application block

Is there any way to log method parameter name , its value and return type value using Enterprise library logging application block.
I have provided a code sample below. The requirement is to log it's methods input parameters value and its return type value
// Complex Types
public class UserDetails
{
public string UserName { get; set; }
public int UserAge { get; set; }
public string UserAddress { get; set; }
}
public class User
{
public string UserId { get; set; }
public string Pwd { get; set; }
}
//Interface
public interface IService
{
UserDetails GetUserDetails(User ReqUser);
}
//Imp
public class Service : IService
{
[LogCallHandler(Categories = new string[] { "General" }, LogBeforeCall = true, LogAfterCall = true ,
BeforeMessage = "This occurs before the call to the target object",AfterMessage="This occured after method call",IncludeParameters=true)]
public UserDetails GetUserDetails(User ReqUser)
{
UserDetails oUD = new UserDetails();
oUD.UserName = "hhh" + ReqUser.UserId;
oUD.UserAge = 100;
oUD.UserAddress = "HHHHHHHHHHHHHHHHHHHHHHH";
return oUD;
}
#endregion
}
//Usage
private void button2_Click(object sender, EventArgs e)
{
IUnityContainer container = new UnityContainer().LoadConfiguration();
container.AddNewExtension<EnterpriseLibraryCoreExtension>();
IService service = container.Resolve<IService>();
User nUser = new User();
nUser.UserId = "TTTTT";
nUser.Pwd = "XXXXX";
UserDetails mm = service.GetUserDetails(nUser);
}
Could anyone please explain how to implement this using Enterprise library logging application block?
You can write an OnMethodBoundaryAspect to intercept your method calls using PostSharp API.
OnMethodBoundaryAspect.OnEntry method includes MethodExecutionArgs parameter which provides all the information you need about the method and its arguments.
See this post for a sample logging aspect implementation very close to your requirements.
// This method is executed before the execution of target methods of this aspect.
public override void OnEntry( MethodExecutionArgs args )
{
// Build method information to log.
string methodInfo = BuildMethodInformation(args.Arguments);
// continue with your logging...
}
You can get method parameters via Arguments member of MethodExecutionArgs parameter like this:
private string BuildMethodInformation(Arguments arguments)
{
var sb = new StringBuilder();
sb.Append(_methodName);
foreach (var argument in arguments.ToArray())
{
sb.Append(arguments.GetArgument( i ) ?? "null");
}
return sb.ToString();
}
For method parameters, check this or this samples. They are built for caching but BuildCacheKey/GetCacheKey methods include all the information you need to get argument information of a method.
You can use EntLib LogCallHandler by code:
container.AddNewExtension<EnterpriseLibraryCoreExtension>();
container.RegisterType<IService, Service>(
new InterceptionBehavior<PolicyInjectionBehavior>(),
new Interceptor<TransparentProxyInterceptor>());
Or by config file:
<unity>
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
<namespace name="LoggingCallHandler" />
<assembly name="LoggingCallHandler" />
<container>
<extension type="Interception" />
<extension type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity.EnterpriseLibraryCoreExtension, Microsoft.Practices.EnterpriseLibrary.Common" />
<register type="IService" mapTo="Service">
<interceptor type="TransparentProxyInterceptor" />
<policyInjection />
</register>
</container>
</unity>
Here, LoggingCallHandler is namespace/assembly for your service class. Alternatively, you can define your type alias like this:
<alias alias="Service" type="LoggingCallHandler.Service, LoggingCallHandler"/>
<alias alias="IService" type="LoggingCallHandler.IService, LoggingCallHandler"/>
See this or this discussion for full configuration including logging block configuration.