I know about those MVVM purists and PasswordBox binding problem but I'm handling my login via codebehind and it works but for the life of me I can't get over using the DataContext from codebehind.
This event is attached to the PasswordChanged event of my passwordbox. Can this be handled in another way?
private void CopyPassword(object sender, RoutedEventArgs e)
{
if(e.Source is PasswordBox)
{
var source = e.Source as PasswordBox;
if(source.DataContext is LoginViewModel)
{
var vm = source.DataContext as LoginViewModel;
vm.Password = source.Password;
}
}
}
Yes it can be handled in many ways. There is nothing wrong with code-behind. Your current solution has the problem that you're tying concrete viewmodel with PasswordBox which makes it NON reusable for other viewmodels.
Better way is to write attached property with event listeners.
There are many resources that can get you started, check the related links on right.
http://www.wpftutorial.net/PasswordBox.html
Related
I'm new to Blazor and trying to make a page with several separate components to handle a massive form. Each individual component covers a part of the form.
The problem I'm facing is that each of my components needs access to data from the back-end, and not every component uses the same data. When the page loads, each components makes an attempt to fetch data from the server, which causes a problem with Entity Framework.
A second operation started on this context before a previous operation
completed. This is usually caused by different threads using the same
instance of DbContext.
This is obviously caused by the fact that my components are initialized at the same time, and all make their attempt to load the data simultaneously. I was under the impression that the way DI is set up in Blazor, this wouldn't be a problem, but it is.
Here are the components in my template:
<CascadingValue Value="this">
<!-- BASE DATA -->
<CharacterBaseDataView />
<!-- SPECIAL RULES -->
<CharacterSpecialRulesView />
</CascadingValue>
Here is how my components are initialized:
protected async override Task OnInitializedAsync()
{
CharacterDetailsContext = new EditContext(PlayerCharacter);
await LoadCharacterAsync();
}
private async Task LoadCharacterAsync()
{
PlayerCharacter = await PlayerCharacterService.GetPlayerCharacterAsync(ViewBase.CharacterId.Value);
CharacterDetailsContext = new EditContext(PlayerCharacter);
}
When two components with the above code are in the same view, the mentioned error occurs. I thread using the synchronous version "OnInitialized()" and simply discarding the task, but that didn't fix the error.
Is there some other way to call the data so that this issue doesn't occur? Or am I going about this the wrong way?
You've hit a common problem in using async operations in EF - two or more operations trying to use the same context at once.
Take a look at the MS Docs article about EF DBContexts - there's a section further down specific to Blazor. It explains the use of a DbContextFactory and CreateDbContext to create contexts for units-of-work i.e. one context per operation so two async operations each have a separate context.
Initially to solve the threading issues, I used DbContextFactory to create contexts for each operation - however this resulted in database in-consistency issues across components, and I realised I need change tracking across components.
Therefore instead, I keep my DbContext as scoped, and I don't create a new context before each operation.
I then adapted my OnInitializedAsync() methods to check if the calls to the database have completed, before making these calls through my injected services. This works really well for my app:
#code {
static Semaphore semaphore;
//code ommitted for brevity
protected override async Task OnInitializedAsync()
{
try
{
//First open global semaphore
semaphore = Semaphore.OpenExisting("GlobalSemaphore");
while (!semaphore.WaitOne(TimeSpan.FromTicks(1)))
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
//If while loop is exited or skipped, previous service calls are completed.
ApplicationUsers = await ApplicationUserService.Get();
}
finally
{
try
{
semaphore.Release();
}
catch (Exception ex)
{
Console.WriteLine("ex.Message");
}
}
}
What is the correct way to open a View in mvvmcross from a non-view? From within a viewmodel we would use ShowViewModel<>(..).
Specifically we are responding to a push notification opening the app (with a custom payload) which dictates a view that should be loaded.
We have a hackety workaround just for proof of concept, just wanted to get an idea of the correct MVX approach
I don't think there is a 'correct way' - I think it depends on your app and what you need it to do.
For some specific cases - e.g. ViewModel->ViewModel and AppStart - MvvmCross provides some convenient methods:
you can call ShowViewModel in MvxViewModel
the app start can be overridden to use a hint object - see https://speakerdeck.com/cirrious/appstart-in-mvvmcross
But overall, any class can request a ShowViewModel by calling:
var viewDispatcher = Mvx.Resolve<IMvxViewDispatcher>();
viewDispatcher.ShowViewModel(new MvxViewModelRequest(
viewModelType,
parameterBundle,
presentationBundle,
requestedBy));
Further, there is a base class - MvxNavigatingObject.cs - which can help with this (it's a base class of MvxViewModel and MvxAppStart) - so you can easily provide one or more services like INavigateMyselfService who's implementations inherit from MvxNavigatingObject.
public interface INavigateMyselfService
{
void GoWild(string side);
}
public class NavigateMyselfService
: MvxNavigatingObject
, INavigateMyselfService
{
public void GoWild(string side)
{
ShowViewModel<WildViewModel>(new { side = side });
}
}
http://forums.xamarin.com/discussion/4694/conditionally-call-registerappstart-with-mvvmcross
Check the above link and you will get idea
In my case,I want to launch the app from secondary tile.For this,I have to launch specific page for Secondary tile.
My Initial App Start view model is LoginViewModel and my custom app start view model is HomeViewModel.
I controlled this from App.cs (Core) to MyCustomAppStart class.
Refer the above link
I'm trying to handle the event when the close button of a Window is clicked:
// View Code
#Override
public void attachWindowListener(WindowListener listener) {
window.addWindowListener(listener);
}
// Presenter code
view.attachWindowListener(new WindowListener(){
public void windowHide(WindowEvent we) {
GWT.log("Window Event - Processing fields");
processFields();
}
});
However, the windowHide function seems to be not executed since I can't see the log I placed there.
How to properly handle that event?
How about
Window.addCloseHandler(
new CloseHandler<Window>()
{
public void onClose( CloseEvent<Window> windowCloseEvent )
{
// Do your worst here
}
} );
I usually put this in onModuleLoad() in my EntryPoint class.
Cheers,
Based on the information provided I would guess that either a.) the events you think are firing do not fire for the Window component (even if it seems like they should) or b.) the events are firing but in a different order than you expect.
For example, it's possible that a BrowserEvent or some other event is firing first as the window is being closed and the Window object's WindowEvent never fires. According to the API docs for GXT 2.x, the WindowEvent will fire on hide and deactivate but it does not specify that it fires on close. The GXT 3.0.x API doc is less clear on this point but I would assume the same behavior. Unfortunately Sencha does not provide good documentation on what events fire for a given component and in what order.
With that said, I have had some luck working through similar issues to this by using a debug class which outputs all the events on a component to which it is attached. This may shed some light on which events are firing and their order of execution, and you may find an optimal event to which you can attach your processFields() method.
For a good example of a debugger class, see this answer from a related post: https://stackoverflow.com/a/2891746/460638. It also includes an example of how to attach the debugger to your component.
API Doc for Window, GXT 2.x: http://dev.sencha.com/deploy/gxt-2.2.5/docs/api/com/extjs/gxt/ui/client/widget/Window.html
API Doc for Window, GXT 3.0.x: http://dev.sencha.com/deploy/gxt-3.0.0/javadoc/gxt/com/sencha/gxt/widget/core/client/Window.html
This worked:
window.addListener(Events.Hide, new Listener<ComponentEvent>() {
#Override
public void handleEvent(ComponentEvent be) {
// Do stuff
}
});
I've been looking at a view examples of the typical "raise dialog from viewmodel" problem, noting 3 main solutions:
use attached behaviors
use a mediator pattern
use a service
I'm getting a bit bogged down though and struggling to find a solution that easily fits into my problem space - which is a very simple file copy problem:
My viewmodel is processing a loop (copying a list of files)
When a file already exists at the destination I need to raise a modal dialog to get confirmation to replace
The vm needs to wait for and receive confirmation before continuing
The "modal dialog" is actually not a new window but a hidden overlay in my MainWindow, as per http://www.codeproject.com/KB/WPF/wpfmodaldialog.aspx (thanks Ronald!)
I'm mostly there but the biggest struggles I have are:
- how to pause the loop in the viewmodel while it waits for input
- how to get input back to the viewmodel within the loop so it can carry on
So far I'm leaning towards the service solution because it seems a direct method call with a return that the vm must wait for. However, it does mean the service needs to tie directly to the view in order to make an element visible?
If anyone can post some simple code that deals directly with this problem I (and the net) would be very happy! Thanks!
For example, you have a service called IDialogService with the following interface:
public interface IDialogService
{
bool ConfirmAction(string title, string confirmationText);
}
As you mentioned, in order for the service to be able to show the actual dialog it needs to have a reference to the view that will show the actual overlay element. But instead of directly referencing the view I prefer to reference it via an interface. Lets call it ICanShowDialog and it will have the following members:
public interface ICanShowDialog
{
void ShowDialog(object dialogContent);
void HideDialog();
}
This interface will be implemented by your view that owns the dialog overlay (e.g. your main window).
Now the interesting part: suspending the code execution while the dialog is shown. First of all, I would recommend you not to use overlay elements but use usual windows if possible. Then you will not have that problem. You can style the dialog window so it will look just like the overlay element.
Anyway, if you still want to use overlay elements then you can do the following trick to suspend the code execution:
Here is pseudo code of the ConfirmAction method of the IDialogService inteface:
public bool ConfirmAction(string title, string confirmationText)
{
ConfirmationDialogView dialogView = new ConfirmationDialogView(title, confirmationText);
DialogShower.ShowDialog(dialogView); // DialogShower is of type ICanShowDialog
while (!dialogView.ResultAvailable)
{
DispatcherUtils.DoEvents();
}
DialogShower.HideDialog();
return dialogView.Result;
}
Here is the code of DispatcherUtils.DoEvents() (that was taken from here: http://dedjo.blogspot.com/2007/08/how-to-doevents-in-wpf.html):
public static class DispatcherUtils
{
public static void DoEvents()
{
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.Background,
(SendOrPostCallback)delegate(object arg) {
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue=True;
}, f);
Dispatcher.PushFrame(frame);
}
}
But I must warn you. Using DoEvents can result in some subtle bugs caused by inner dispatcher loops.
As an alternative to suspending the code execution while a dialog is shown you can use callbacks:
public interface IDialogService
{
void ConfirmAction(string title, string confirmationText, Action<bool> dialogResultCallback);
}
But it will not be so convenient to use.
We're trying to play around with RIA Services. I can't seem to figure out how to delete a record. Here's the code I'm trying to use.
SomeDomainContext _SomeDomainContext = (SomeDomainContext)(productDataSource.DomainContext);
Product luckyProduct = (Product)(TheDataGrid.SelectedItem);
_SomeDomainContext.Products.Remove(luckyProduct);
productDataSource.SubmitChanges();
The removing the object from the Entity part works fine, but it doesn't seem to do anything to the DB. Am I using the objects like I'm supposed to, or is there a different way of saving things?
The error system is a little finicky. Try this o get the error if there is one and that will give you an idea. My problem was dependencies to other tables needing deletion first before the object could be. Ex: Tasks deleted before deleting the Ticket.
System.Windows.Ria.Data.SubmitOperation op = productDataSource.SubmitChanges();
op.Completed += new EventHandler(op_Completed);
void TicketsLoaded_Completed(object sender, EventArgs e) {
System.Windows.Ria.Data.SubmitOperation op = (System.Windows.Ria.Data.SubmitOperation)sender;
if (op.Error != null) {
ErrorWindow view = new ErrorWindow(op.Error);
view.Show();
}
}
In the code snippet above, I'd suggest using the callback parameter rather than an event handler.
productsDataSource.SubmitChanges(delegate(SubmitOperation operation) {
if (operation.HasError) {
MessageBox.Show(operation.Error.Message);
}
}, null);
The callback model is designed for the caller of Load/SubmitChanges, while the event is designed for other code that gets a reference to a LoadOperation/SubmitOperation.
Hope that helps...