User notification MVVM - xaml

I am trying to create a user notification. Ideally a toast-like notification that shows up in the corner for about three seconds.
I am using MVVM-light and I think the notification could be done using its messenger-service.
I have this class:
public class NotificationSync
{
public string Messages { get; set; }
}
In one viewmodel i set up the Messenger like this:
Messenger.Default.Send(new NotificationSync()
{
Messages = "message"
});
And in my MainviewModel (which is the datacontext of the view) I listen for it like this:
Messenger.Default.Register<NotificationSync>(this, (action) =>
Mess = action.Messages );
Mess is a string property on the viewmodel:
private string mess;
public string Mess
{
get { return mess; }
set
{
mess = value;
RaisePropertyChanged("Mess");
}
}
What I would like to do with mess is to bind it to my view in a toast-like manner. I.E display it for some seconds in my view. Any tips on how to do this? Thank you.

What about a Visibility property for your toast plus a timer?
Messenger.Default.Register<NotificationSync>(this, (action) =>
Mess = action.Messages
ShowToast();
);
private void ShowToast()
{
IsToastVisible = true;
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = TimeSpan.FromSeconds(3);
dispatcherTimer.Start();
}
void OnTimerTick(Object sender, EventArgs args)
{
IsToastVisible = false;
}
This assumes the textbox to which Mess is bound, is also bound to IsToastVisible and it's using a VisibilityConverter.

Related

Hit Break Point Goes Upward Blazor Server

I have a Language dropdown in Blazor layout that finally calls this method:
private async Task SetLanguage(string lang)
{
cultureChanger.ChangeCulture(lang);
await GetLanguageValues();
}
The GetLanguageValues() method should be called then, But the debugger does not step over cultureChanger.ChangeCulture(lang); and then goes out of the method to the caller. How do I solve this?
GetLanguageValues method:
string lang = "";
private async Task GetLanguageValues()
{
lang = textService.GetText(cultureChanger.Current, "lang");
ViewData.Language = lang;
StateHasChanged();
}
CultureChanger class:
public class CultureChanger
{
public CultureInfo Current { get; private set; } = new CultureInfo("en");
public event Action OnCultureChange;
public void ChangeCulture(string cultureName)
{
Current = new CultureInfo(cultureName);
OnCultureChange?.Invoke();
}
}
I am upgrading a preview 6 Blazor project to preview 9, Previously it was working.
Solved:
One of the components that subscribe to this event had StateHasChanged() in its method that I replaced with: base.InvokeAsync(StateHasChanged);

NotifyPropertyChange fired but UI field not updated in Xamarin.Forms

I'm currently refactoring a few abstraction layers into a Xamarin app in order to break the "monolithic" structure left by the previous dev, but something has gone awry. In my ViewModel, I have a few properties that call NotifyPropertyChange in order to update the UI whenever a value is picked from a list. Like so:
public Notifier : BindableObject, INotifyPropertyChanged
{
//...
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Had to create a middle layer due to my specific needs
public interface ISomeArea
{
DefinicaoServicoMobile TipoPasseio { get; set; }
}
-
public class SomeAreaImpl : Notifier, ISomeArea
{
//...
protected DefinicaoServicoMobile _tipoPasseio;
public DefinicaoServicoMobile TipoPasseio
{
get => _tipoPasseio;
set
{
if (_tipoPasseio != value)
{
_tipoPasseio = value;
NotifyPropertyChanged(nameof(TipoPasseio));
}
}
}
}
The actual bound view model:
public MyViewModel : BaseViewModel, ISomeArea
{
private SomeAreaImpl someArea;
//...
public MyViewModel()
{
// This is meant to provide interchangable areas across view models with minimal code replication
someArea = new SomeAreaImpl();
}
public DefinicaoServicoMobile TipoPasseio
{
get => someArea.TipoPasseio;
set => someArea.TipoPasseio = value;
}
}
And the .xaml snippet:
<renderers:Entry
x:Name="TxtTipoPasseio"
VerticalOptions="Center"
HeightRequest="60"
HorizontalOptions="FillAndExpand"
Text="{Binding TipoPasseio.DsPadrao}"
/>
The renderer opens a list allowing the user to choose whichever "TipoPasseio" they want, and supposedly fill the textbox with a DsPadrao (standard description). Everything works, even the reference to TipoPasseio is held after being selected (I know this because should I bring up the list a second time, it will only display the selected DsPadrao, giving the user the option to clean it. If he does, a third tap will show all the options again.
I might have screwed up in the abstraction, as I don't see the setter for myViewModel.TipoPasseio being called, tbh
Any ideas?
Let's reason through what Xamarin knows (as best as we can, since you didn't include all of the relevant code):
You have a data context having the type MyViewModel
That view model object has a property named TipoPasseio, having type DefinicaoServicoMobile
The type DefinicaoServicoMobile has a property named DsPadrao
It is that last property that is bound to the Entry.Text property.
In a binding, any observable changes to values forming the source or path for the binding will cause the runtime to update the target property for the binding (Entry.Text) and thus result in a change in the visual appearance (i.e. new text being displayed).
Note the key word observable. Here are the things I see which are observable by Xamarin:
The data context. But this doesn't change.
That's it.
With respect to the value of the MyViewModel.TipoPasseio property, there's nothing in the code you posted showing this property changing. But if it did, it doesn't look like MyViewModel implements INotifyPropertyChanged, so Xamarin wouldn't have a way to observe such a change.
On that second point, you do implement INotifyPropertyChanged in the SomeAreaImpl type. But Xamarin doesn't know anything about that object. It has no reference to it, and so has no way to subscribe to its PropertyChanged event.
Based on your statement:
I don't see the setter for myViewModel.TipoPasseio being called
That suggests that the TipoPasseio property isn't being changed. I.e. while you wouldn't be providing notification to Xamarin even if it did change, it's not changing anyway.
One property that does seem to be changing is the DsPadrao property (after all, it's the property that's actually providing the value for the binding). And while you don't provide enough details for us to know for sure, it seems like a reasonable guess that the DefinicaoServicoMobile doesn't implement INotifyPropertyChanged, and so there's no way for Xamarin to ever find out the value of that property might have changed either.
In other words, of all the things that Xamarin can see, the only one that it would be notified about of a change is the data context. And that doesn't seem to be what's changing in your scenario. None of the other values are held by properties backed by INotifyPropertyChanged.
Without a complete code example, it's impossible to know for sure what the right fix is. Depending on what's changing and how though, you need to implement INotifyPropertyChanged for one or more of your types that don't currently do so.
As it turns out, I wasn't firing the NotifyPropertyChanged of the correct object. Both MyViewModel and SomeAreaImpl implemented INotifyPropertyChanged per the Notifier class as BaseViewModel also extends from Notifier but that ended up ommited in my question. Having figured that out, here's an working (and complete) example:
public Notifier : BindableObject, INotifyPropertyChanged
{
//...
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Specifics about DefinicaoServicoMobile are negligible to this issue
public interface ISomeArea
{
//...
DefinicaoServicoMobile TipoPasseio { get; set; }
Task SetServico(ServicoMobile servicoAtual;
//...
}
For the sake of clarification
public abstract class BaseViewModel : Notifier
{
protected abstract Task SetServico(ServicoMobile servicoAtual);
public async Task SetServico()
{
//...
await SetServico(servicoAtual);
//...
}
}
Changed a couple of things here. It no longer extends from Notifier, which was kinda weird to begin with. Also this is where I assign TipoPasseio
public class SomeAreaImpl : ISomeArea
{
//...
protected DefinicaoServicoMobile _tipoPasseio;
// I need to call the viewModel's Notifier, as this is the bound object
private BaseViewModel viewModel;
public AreaServicosDependentesImpl(BaseViewModel viewModel)
{
this.viewModel = viewModel;
}
public DefinicaoServicoMobile TipoPasseio
{
get => _tipoPasseio;
set
{
if (_tipoPasseio != value)
{
_tipoPasseio = value;
viewModel.NotifyPropertyChanged(nameof(TipoPasseio));
}
}
}
//Assigning to the property
public async Task SetServico(ServicoMobile servicoAtual, List<DefinicaoServicoMobile> listDefinicaoServico)
{
//...
TipoPasseio = listDefinicaoServico
.FirstOrDefault(x => x.CdServico == servicoAtual.TpPasseio.Value);
//...
}
}
Changes to the view model:
public MyViewModel : BaseViewModel, ISomeArea
{
private SomeAreaImpl someArea;
//...
public MyViewModel()
{
someArea = new SomeAreaImpl(this);
}
public DefinicaoServicoMobile TipoPasseio
{
get => someArea.TipoPasseio;
set => someArea.TipoPasseio = value;
}
protected override async Task SetServico(ServicoMobile servicoAtual)
{
//...
someArea.SetServico(servicoAtual, ListDefinicaoServico.ToList());
//...
}
}
View model binding
public abstract class BaseEncerrarPontoRotaPage : BasePage
{
private Type viewModelRuntimeType;
public BaseEncerrarPontoRotaPage(Type viewModelRuntimeType)
{
this.viewModelRuntimeType = viewModelRuntimeType;
}
private async Task BindContext(PontoRotaMobile pontoRota, ServicoMovelMobile servicoMovel, bool finalizar)
{
_viewModel = (BaseViewModel)Activator.CreateInstance(viewModelRuntimeType, new object[] { pontoRota, UserDialogs.Instance });
//...
await _viewModel.SetServico();
//...
BindingContext = _viewModel;
}
public static BaseEncerrarPontoRotaPage Create(EnumAcaoServicoType enumType)
{
Type pageType = enumType.GetCustomAttribute<EnumAcaoServicoType, PageRuntimeTypeAttribute>();
Type viewModelType = enumType.GetCustomAttribute<EnumAcaoServicoType, ViewModelRuntimeTypeAttribute>();
return (BaseEncerrarPontoRotaPage)Activator.CreateInstance(pageType, new object[] { viewModelType });
}
}
Page instantiation is performed in some other view model, not related to the structure presented here
private async Task ShowEdit(bool finalizar)
{
await Task.Run(async () =>
{
var idAcaoServico = ServicoMobileAtual.DefinicaoServicoMobile.IdAcaoServico;
var page = BaseEncerrarPontoRotaPage.Create((EnumAcaoServicoType)idAcaoServico);
await page.BindContext(PontoRotaAtual, ServicoMovelMobileAtual, finalizar);
BeginInvokeOnMainThread(async () =>
{
await App.Navigation.PushAsync(page);
});
});
}
Codebehind:
public partial class MyPage : BaseEncerrarPontoRotaPage
{
public NormalUnidadePage() { }
public MyPage(Type viewModelType) : base(viewModelType)
{
InitializeComponent();
//Subscription to show the list
TxtTipoPasseio.Focused += TxtTipoPasseio_OnFocused;
//...
}
}
XAML
<views:BaseEncerrarPontoRotaPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:My.Name.Space.;assembly=Phoenix.AS"
x:Class="My.Name.Space.MyPage">
//...
<renderers:Entry
x:Name="TxtTipoPasseio"
VerticalOptions="Center"
HeightRequest="60"
HorizontalOptions="FillAndExpand"
Text="{Binding TipoPasseio.DsPadrao}"/>
//...
</views:BaseEncerrarPontoRotaPage>
I know could propagate an event from the AreaImpl classes in order to fire the Notify event in the view model, but right now I'm satisfied with this solution.

Resolve endpoint bindings dynamically in a workflow

I have the same issue as this question on MSDN, but I don't understand the solution because it is still not clear to me if Roman Kiss's solution will correctly replace an endpoint address while a single workflow instance being executed concurrently.
When internal Send activity is scheduled for execution by one thread with certain enpoint address, wouldn't this address be overridden by another thread that schedules same activity with different endpoint address? Correct me if I am mistaken, but I assume it would, because Send.Endpoint is a regular property as oppose to being InArgument<Endpoint> bound to whatever current workflow execution context is.
Can someone shed more light onto this?
UPDATE
I tested the solution provided by Roman Kiss, and it turns out that it is not working as expected in my scenario. I modified Execute method as follows:
protected override void Execute(NativeActivityContext context)
{
Thread.Sleep(Address.Get(context).EndsWith("1") ? 1000 : 0);
Body.Endpoint.Binding = GetBinding(Binding.Get(context));
Body.Endpoint.AddressUri = new Uri(Address.Get(context));
Thread.Sleep(Address.Get(context).EndsWith("1") ? 0 : 3000);
var address = Address.Get(context) + " => " + Body.Endpoint.AddressUri;
Console.WriteLine(address);
Thread.Sleep(10000);
context.ScheduleActivity(Body);
}
Ran this test:
static void Main(string[] args)
{
// Workflow1 is just a SendScope wrapped around by a Sequence with single Address input argument exposed
var workflow = new Workflow1();
Task.WaitAll(
Task.Run(() => WorkflowInvoker.Invoke(workflow, new Dictionary<string, object> { { "Address", #"http://localhost/1" } })),
Task.Run(() => WorkflowInvoker.Invoke(workflow, new Dictionary<string, object> { { "Address", #"http://localhost/2" } })));
Console.ReadLine();
}
The result I am getting is:
http://localhost/1 => http://localhost/1
http://localhost/2 => http://localhost/1
The question remains open: how do I assign endpoint address of my Send activity dynamically at runtime?
This will work as shown because a new Send activity is created by the factory and so when using the CacheMetadata method to setup that Send activity it is setting the binding properly on that instance of the activity.
Including Content Incase Link Dies
[ContentProperty("Body")]
public class SendScope : NativeActivity
{
[DefaultValue((string)null)]
[RequiredArgument]
public InArgument<string> Binding { get; set; }
[DefaultValue((string)null)]
[RequiredArgument]
public InArgument<string> Address { get; set; }
[Browsable(false)]
public Send Body { get; set; }
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
if (this.Body == null || this.Body.EndpointAddress != null)
{
metadata.AddValidationError("Error ...");
return;
}
this.Body.Endpoint = new Endpoint()
{
AddressUri = new Uri("http://localhost/"),
Binding = new BasicHttpBinding(),
ServiceContractName = this.Body.ServiceContractName
};
metadata.AddChild(this.Body);
base.CacheMetadata(metadata);
}
protected override void Execute(NativeActivityContext context)
{
this.Body.Endpoint.Binding = GetBinding(this.Binding.Get(context));
this.Body.Endpoint.AddressUri = new Uri(this.Address.Get(context));
context.ScheduleActivity(Body);
}
private System.ServiceModel.Channels.Binding GetBinding(string binding)
{
if (binding == "basicHttpBinding")
return new BasicHttpBinding();
//else ... others bindings
return null;
}
}
public class SendScopeFactory : IActivityTemplateFactory
{
public Activity Create(DependencyObject target)
{
return new SendScope()
{
DisplayName = "SendScope",
Body = new Send()
{
Action = "*",
OperationName = "ProcessMessage",
ServiceContractName = "IGenericContract",
}
};
}
}
Create a custom native activity for setting Send.Endpoint property during the runtime based on your properties such as Binding, Address, Security, etc.
Create designer for this SendScope activity something simular like CorrelationScope
Create SendScopeFactory - see the above code snippet.

Silverlight - DataContext, DataGrid, WCF and adding elements programatically with binding

I'm pretty new to Silverlight.
I have a usercontrol with a datagrid and other form elements.
I'm trying to achieve binding programatically, loading the data from a WCF service.
this is what I have, I'm not sure how to actually make it work:
public class DepartmentObject
{
public Guid Id { get; set; }
public string Name { get; set; }
public IEnumerable<Person> People { get; set; }
}
public partial class DepartmentView : UserControl
{
ObservableCollection<Person> LocalPeople { get; set; }
Service1Client client = new Proj.ServiceReference1.Service1Client();
public TemplateView(Guid departmentId)
{
InitializeComponent();
client.GetDeptCompleted += (sender, e) =>
{
this.DataContext = e.Result; //DepartmentObject w/People member IEnumerable<Person>
this.peopleList.SetBinding(DataGrid.ItemsSourceProperty, new Binding("People"));
LocalPeople = new ObservableCollection<Person>(e.Result.People);
};
client.GetDeptAsync(departmentId);
}
private void Add_Person_Click(object sender, RoutedEventArgs e)
{
LocalPeople.Add(new Person() { Name = String.Format("Person [{0}]", LocalPeople.Count) });
}
}
Now, I understand why it doesn't work - there's no "connection" between the observable collection and the data context, but what is the proper way of doing this?
peopleList is a DataGrid, which will have secondary binding when a person is selected.
I want to be able to hit "Save" eventually and get everything that changed in DepartmentObject.
I've gone through a lot of examples and all the SL 2-3-4 examples are different and confusing.
I'm not sure whether the e.Result is going to get persisted. The following should work though:
client.GetDeptCompleted += (sender, e) =>
{
LocalPeople = new ObservableCollection<Person>(e.Result.People);
this.peopleList.DataContext = LocalPeople; //DepartmentObject w/People member IEnumerable<Person>
this.peopleList.SetBinding(DataGrid.ItemsSourceProperty, new Binding("."));
};
Set a break point after LocalPeople has been assigned to make sure the collection isn't empty.

Setting internal properties in composite WF4 Activities at design time

I want to create a composite Windows Workflow Activity (under .NET 4) that contains a predefined ReceiveAndSendReply Activity. Some of the properties are predefined, but others (particularly ServiceContractName) need to be set in the designer.
I could implement this as an Activity Template (the same way ReceiveAndSendReply is implemented), but would rather not. If I later change the template, I'd have to update all previously created workflows manually. A template would also permit other developers to change properties that should be fixed.
Is there a way to do this from a Xaml Activity? I have not found a way to assign an Argument value to a property of an embedded Activity. If not, what technique would you suggest?
I haven't done this using a composite XAML activity and am getting some errors when I try but doing so through a NativeActivity is no problem. See the example code below.
public class MyReceiveAndSendReply : NativeActivity
{
private Receive _receive;
private SendReply _sendReply;
public string ServiceContractName { get; set; }
public string OperationName { get; set; }
protected override bool CanInduceIdle
{
get { return true; }
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
_receive = _receive ?? new Receive();
_sendReply = _sendReply ?? new SendReply();
_receive.CanCreateInstance = true;
metadata.AddImplementationChild(_receive);
metadata.AddImplementationChild(_sendReply);
_receive.ServiceContractName = ServiceContractName;
_receive.OperationName = OperationName;
var args = new ReceiveParametersContent();
args.Parameters["firstName"] = new OutArgument<string>();
_receive.Content = args;
_sendReply.Request = _receive;
var results = new SendParametersContent();
results.Parameters["greeting"] = new InArgument<string>("Hello there");
_sendReply.Content = results;
base.CacheMetadata(metadata);
}
protected override void Execute(NativeActivityContext context)
{
context.ScheduleActivity(_receive, ReceiveCompleted);
}
private void ReceiveCompleted(NativeActivityContext context, ActivityInstance completedInstance)
{
context.ScheduleActivity(_sendReply);
}
}