I am trying to track navigation between a number of Web Forms in ASP.NET. I've tried the client side back navigation using the following:
<asp:Button ID="BackButton" runat="server" Text="Back"
OnClientClick="JavaScript:window.history.back(1);return false;" />
Unfortunately this does not work for my scenario due to postbacks going on. My scenario has a number of Web Forms:
Page1.1
Page1.2
Page2
Page3
Navigating forward through the pages works similarly to a wizard. There are 2 starting points - from Page1.1 and Page1.2.
Page1.1 -> Page2 -> Page3
Page1.2 -> Page2 -> Page3
So clicking back buttons will have the following navigation:
Page3 -> Page2
Page2 -> Page1.1
Page2 -> Page1.2
There are additional parameters passed between the pages which need to be maintained.
I am currently looking at maintaining something in the Session to maintain the current call stack which somewhat works however, I am getting a build up of referrer urls. At the minute I am just trying to conceptualise this.
I am running this in SharePoint as Application Pages, however each page is essentially an ASP.NET page for the sake of this example.
So I have introduced an abstract class for each Page:
public abstract class SecureLayoutsPageBase : System.Web.UI.Page
{
private PageController _pageController;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_pageController = (PageController)Session["PageController"];
if (_pageController == null)
{
_pageController = new PageController();
Session["PageController"] = _pageController;
}
if (!Page.IsPostBack && Page.Request.UrlReferrer != null)
{
this.PageController.History.Push(Page.Request.UrlReferrer.ToString());
}
}
protected PageController PageController
{
get
{
return _pageController;
}
}
}
Which has an instance of PageController:
[Serializable()]
public class PageController
{
private Stack<string> _history = new Stack<string>();
public void Previous(HttpResponse response)
{
string previous = _history.Pop();
response.Redirect(previous);
}
public Stack<string> History
{
get
{
return _history;
}
}
}
Then each page will call the PageController.Previous in the server side event handler for the back button click:
protected void BackButton_Click(object sender, EventArgs e)
{
this.PageController.Previous(this.Response);
}
This issue with this is that calling PageController.Previous still results in the Url being added to the stack. I am just wondering if there is a way to prevent the url getting added when back has been clicked. Or alternative solutions...
History(-1) wont work, because this will include postbacks. Just set the button href dependant on whatever page you're on. If you know itsloaded page 3, set the back button to page 2
OK... couple of tweaks to get my scenario working. Not keen on this solution so any others would be good.
Change to SecureLayoutsPageBase:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_pageController = (PageController)Session["PageController"];
if (_pageController == null)
{
_pageController = new PageController();
Session["PageController"] = _pageController;
}
if (!Page.IsPostBack && Page.Request.UrlReferrer != null && Page.Request.Url.ToString() != this.PageController.PreviousUrl)
{
this.PageController.AddHistory(Page.Request.UrlReferrer.ToString());
}
}
Change to PageController:
[Serializable()]
public class PageController
{
private Stack<string> _history = new Stack<string>();
private string _previous;
public void Previous(HttpResponse response)
{
_previous = _history.Pop();
response.Redirect(_previous);
}
public void AddHistory(string url)
{
if(url != _previous)
{
_history.Push(url);
}
}
public Stack<string> History
{
get
{
return _history;
}
}
public string PreviousUrl
{
get
{
return _previous;
}
}
}
Related
I'm creating an Xamarin.Forms MVVM App (only using Android) which needs certain buttons to be outlined red, whenever their text property holds a specific value. (Purpose: alert the user to press the button and select a value, which will change the Button Text Property and therefore remove the red outline)
To achieve this I've create the following documents:
A custom button CButton that extents the default Button:
public class CButton : Button
{
// this Hides the Default .Text-Property
public string Text
{
get => base.Text;
set
{
base.Text = value;
TextChangedEvent(this, new EventArgs());
}
}
// The Raised Event
protected virtual void TextChangedEvent(object sender, EventArgs e)
{
EventHandler<EventArgs> handler = TextChanged;
handler(sender, e);
}
public event EventHandler<EventArgs> TextChanged;
}
A custom behavior makes use of the raised TextChangedEvent
public class ButtonValBehavior : Behavior<CButton>
{
protected override void OnAttachedTo(CButton bindable)
{
bindable.TextChanged += HandleTextChanged;
base.OnAttachedTo(bindable);
}
void HandleTextChanged(object sender, EventArgs e)
{
string forbidden = "hh:mm|dd.mm.yyyy";
if (forbidden.Contains((sender as CButton).Text.ToLower()))
{
//Do when Button Text = "hh:mm" || "dd.mm.yyyy"
(sender as CButton).BorderColor = Color.Gray;
}
else
{
//Do whenever Button.Text is any other value
(sender as CButton).BorderColor = Color.FromHex("#d10f32");
}
}
protected override void OnDetachingFrom(CButton bindable)
{
bindable.TextChanged -= HandleTextChanged;
base.OnDetachingFrom(bindable);
}
}
The relevant parts of the ViewModel look the following:
public class VM_DIVI : VM_Base
{
public VM_DIVI(O_BasisProtokoll base)
{
Base = base;
}
private O_BasisProtokoll _base = null;
public O_BasisProtokoll Base
{
get => _base;
set
{
_base = value;
OnPropertyChanged();
}
}
Command _datePopCommand;
public Command DatePopCommand
{
get
{
return _datePopCommand ?? (_datePopCommand = new Command(param => ExecuteDatePopCommand(param)));
}
}
void ExecuteDatePopCommand(object param)
{
//launch popup
var p = new PP_DatePicker(param);
PopupNavigation.Instance.PushAsync(p);
}
}
The .xmal looks the following (b is the xmlns of the Namespace):
<b:CButton x:Name="BTN_ED_Datum"
Text="{Binding Base.ED_datum, Mode=TwoWay}"
Grid.Column="1"
Command="{Binding DatePopCommand}"
CommandParameter="{x:Reference BTN_ED_Datum}">
<b:CButton.Behaviors>
<b:ButtonValBehavior/>
</b:CButton.Behaviors>
</b:CButton>
This solution works fine whenever the input is caused by user interaction. However, when a Value is assigned during the initialization of the Page no red outline is created, in fact the TextChangedEvent isn't raised. By using breakpoints I noticed that during initialization the Text Property of CButton is never set, eventhough it actually will be in the view.
Despite fiddling around with my solution I cannot make this work on initialization. I tried to work around this issue by outlining every button by default in their constructor, however this will outline every button red, even when their text value doesn't require them to be.
How can I achieve my initial goal?
Many thanks in advance!
It's been a while but if I recall correctly what I ended up doing was:
Changing the new Text-Property of my custom Button to CText and
Making sure that I have Mode=TwoWay activated for any Element, that doesn't have it enabled by default. (Look up Binding modes on msdn for more)
making CText a bindable property of CButton
My custom button now looks the following:
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace EORG_Anton.Model
{
public class CButton : Button
{
public static readonly BindableProperty CTextProperty =
BindableProperty.Create(nameof(CText),
typeof(string),
typeof(CButton),
default(string),
BindingMode.TwoWay,
propertyChanged: OnTextChanged);
private static void OnTextChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (CButton)bindable;
var value = (string)newValue;
control.CText = value;
}
public string CText
{
get => base.Text;
set
{
base.Text = value;
TextChangedEvent(this, new EventArgs());
}
}
protected virtual void TextChangedEvent(object sender, EventArgs e)
{
EventHandler<EventArgs> handler = TextChanged;
handler(sender, e);
}
public event EventHandler<EventArgs> TextChanged;
}
}
I need to display DisplayAlert from the View Model, however its simply doesn't display. Is there some other way how to display alert from the VM? The permission is true so that works.
private async Task TakePicture()
{
await Permission();
var imageSource = Application.Current.MainPage.DisplayActionSheet(AppResources.AlertNewPhoto, AppResources.AlertNewPhoto, AppResources.AlertGallery);
if (imageSource.Result == AppResources.AlertNewPhoto)
}
You can change your constructor of ViewModel like following code.
public PersonsViewModel(ContentPage page){
page.DisplayAlert("info","test","Ok");
}
In your Layout background code, you can use it following code.
public partial class MainPage : ContentPage
{
PersonsViewModel personsViewModel;
public MainPage()
{
InitializeComponent();
personsViewModel = new PersonsViewModel(this);
this.BindingContext = personsViewModel;
}
If you can use plugin, you can use ACR.UserDialogs. https://github.com/aritchie/userdialogs
I solved this problem using events
public MainPageVewModel()
{
Application.Current.MainPage.Loaded += LoadCards;
}
private async void LoadCards(object sender, EventArgs e)
{
// your code on View Loaded
await Application.Current.MainPage.DisplayAlert("working alert", "alert", "ok");
}
How could I detect tab was touched in xamarin forms TabbedPage?
(which is different from page changed detection which I figured how to detect)
Here is why:
I'm trying to work around a rather ugly tabbed page overflow UI
(the ugly scroller that shows up on the right over the tabbar
whenever there are >5 tabs)
So the 5th tab press shows a custom menu, second press hides that menu, etc.
Thanks!
If you are trying to find which page is selected in TabbedPage you could do it in this way.
With Index value you can perform whatever action you want..
Event for detecting page no:
this.CurrentPageChanged += (object sender, EventArgs e) => {
var i = this.Children.IndexOf(this.CurrentPage);
System.Diagnostics.Debug.WriteLine("Page No:"+i);
};
It has been a while since this was asked but just in case here is an answer.
Perform an action when a tab is tapped is the same as when:
The tab has changed
The tab is "reselected"
So for the first one you can use #femil-shajin 's answer or as I do here which is more direct and for the second one you need to make some custom renderers for the TabbedPage:
public class MyTabbedPage : TabbedPage
{
...
protected override void OnCurrentPageChanged()
{
// do whatever
}
public void OnTabReselected()
{
// do whatever
}
...
}
Then on Android:
[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedRenderer))]
namespace MyNamespace.Droid.Renderers
{
public class CustomTabbedRenderer : TabbedPageRenderer, NavigationBarView.IOnItemReselectedListener
{
...
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
GetBottomNavigationView()?.SetOnItemReselectedListener(this);
}
}
private BottomNavigationView GetBottomNavigationView()
{
// this may need to change on some cases
for (var i = 0; i < ViewGroup.ChildCount; i++)
{
var childView = ViewGroup.GetChildAt(i);
if (childView is ViewGroup viewGroup)
{
for (var j = 0; j < viewGroup.ChildCount; j++)
{
var childRelativeLayoutView = viewGroup.GetChildAt(j);
if (childRelativeLayoutView is BottomNavigationView bottomNavigationView)
{
return bottomNavigationView;
}
}
}
}
return null;
}
public void OnNavigationItemReselected(IMenuItem item)
{
if (Element is MyTabbedPage tabbedPage)
{
tabbedPage.OnTabReselected();
}
}
...
}
}
And on iOS:
[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedRenderer))]
namespace MyNamespace.iOS.Renderers
{
public class CustomTabbedRenderer : TabbedRenderer
{
private UITabBarItem _previousSelectedItem;
...
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
if (SelectedIndex < TabBar.Items.Length)
{
_previousSelectedItem = TabBar.Items[SelectedIndex];
}
}
public override void ItemSelected(UITabBar tabbar, UITabBarItem item)
{
if (_previousSelectedItem == item && Element is MyTabbedPage tabbedPage)
{
tabbedPage.OnTabReselected();
}
_previousSelectedItem = item;
}
...
}
}
Source: Part of this was based on this page
During my override of OnActivate() in my view-model, I need to call GetView() in order to focus an element. When I do this after I have previously activated my view, it's fine. But when I call this the first activation, it fails.
I was able to get it to work by swapping a few lines in ConductorBaseWithActiveItem.ChangeActiveItem. The original is as follows:
protected virtual void ChangeActiveItem(T newItem, bool closePrevious) {
ScreenExtensions.TryDeactivate(activeItem, closePrevious);
newItem = EnsureItem(newItem);
if(IsActive)
ScreenExtensions.TryActivate(newItem);
activeItem = newItem;
NotifyOfPropertyChange("ActiveItem");
OnActivationProcessed(activeItem, true);
}
and with my changes:
protected virtual void ChangeActiveItem(T newItem, bool closePrevious) {
ScreenExtensions.TryDeactivate(activeItem, closePrevious);
newItem = EnsureItem(newItem);
activeItem = newItem;
NotifyOfPropertyChange("ActiveItem");
if (IsActive)
ScreenExtensions.TryActivate(newItem);
OnActivationProcessed(activeItem, true);
}
This seems to work. Notifying that "ActiveItem" changed triggers the code to load and cache the view. Then ScreenExtensions.TryActivate calls my OnActivate override.
Question: I haven't noticed any problems doing this, but I'm curious if anyone knows better than I do what repercussions this change could have?
Thanks!
One thing you could try is overriding Caliburn's OnViewAttached method and trying to focus it there. That being said, in MVVM, focus is more of a View concern, so if possible, that logic should be moved from the ViewModel to the View.
One way you may be able to solve this is by creating an attached behavior (you will need a reference to the Microsoft.Expression.Interactions assembly):
public class FocusWhenVisibleBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
this.AssociatedObject.Loaded += this.Loaded;
this.AssociatedObject.IsVisibleChanged += this.VisibleChanged;
}
protected override void OnDetaching()
{
this.AssociatedObject.Loaded -= this.Loaded;
this.AssociatedObject.IsVisibleChanged -= this.VisibleChanged;
}
private void Loaded(object sender, RoutedEventArgs e)
{
this.TryFocus();
}
private void VisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
this.TryFocus();
}
private void TryFocus()
{
if (this.AssociatedObject.IsLoaded && this.AssociatedObject.IsVisible)
{
// Focus the control
this.AssociatedObject.Focus();
}
}
}
And that attaching that behavior to whatever control you want to focus:
<Button>
<i:Interaction.Behaviors>
<b:FocusWhenVisibleBehavior/>
</i:Interaction.Behaviors>
</Button>
I want to know whether how to capture the button clicked with AspectJ and get its parameter (eg. button name). I think for having more generalized capturing with AspectJ, it shoudl be used MouseListener so it can capture other UI elements in general!
Example:
In a GUI example I have defined 2 buttons that take some actions
public JButton btn1 = new JButton("Test1");
public JButton btn2 = new JButton("Test2");
btn1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//take some actions
}
}
btn2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//take some actions
}
}
How to capture these buttons with AspectJ, and get their parameters (eg. name)?
It is possible. I have provided two examples. The first that prints out for every JButton that has an ActionListener. The other example only prints out if a specific buttons is clicked.
Prints the text for every JButton clicked with an ActionListener:
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent)")
public void buttonPointcut(ActionEvent actionEvent) {}
#Before("buttonPointcut(actionEvent)")
public void beforeButtonPointcut(ActionEvent actionEvent) {
if (actionEvent.getSource() instanceof JButton) {
JButton clickedButton = (JButton) actionEvent.getSource();
System.out.println("Button name: " + clickedButton.getText());
}
}
Prints the text for a specific JButton:
public static JButton j1;
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent) && if()")
public static boolean button1Pointcut(ActionEvent actionEvent) {
return (actionEvent.getSource() == j1);
}
#Before("button1Pointcut(actionEvent)")
public void beforeButton1Pointcut(ActionEvent actionEvent) {
// logic before the actionPerformed() method is executed for the j1 button..
}
UPDATED:
You can do this in many different ways. For example add your buttons to the aspect directly. But I prefere to use a enum object between (ButtonManager in this case), so the code does not know about the aspect. And since the ButtonManager is an enum object, it is easy for the aspect to retrieve values from it.
I just tested it with a Swing button class from Oracle and it works. In the Swing class:
b1 = new JButton("Disable middle button", leftButtonIcon);
ButtonManager.addJButton(b1);
AspectJ is extremely powerful when it comes to manipulating classes, but it can not weave advises into specific objects since objects is not created at the time of weaving. So you can only work with objects at runtime and that is why I have added the addJButton(..) method above. That enables the aspect to check the advised button against a list of registered buttons.
The ButtonManager class:
public enum ButtonManager {
;
private static Collection<JButton> buttonList = new LinkedList<JButton>();
public static void addJButton(JButton jButton) {
buttonList.add(jButton);
}
public static Collection<JButton> getButtonList() {
return buttonList;
}
}
Modified pointcut and advice to only print the name of the buttons registered in the ButtonManager:
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent) && if()")
public static boolean buttonListPointcut(ActionEvent actionEvent) {
Collection<JButton> buttonList = ButtonManager.getButtonList();
JButton registeredButton = null;
for (JButton jButton : buttonList) {
if (actionEvent.getSource() == jButton) {
registeredButton = jButton;
}
}
return registeredButton != null;
}
#Before("buttonListPointcut(actionEvent)")
public void beforeButtonListPointcut(ActionEvent actionEvent) {
JButton clickedButton = (JButton) actionEvent.getSource();
System.out.println("Registered button name: " + clickedButton.getText());
}
UPDATED 2
Okay, I believe I understand what you want. You want to listen to mouse events. That is possible. The downside is that you have to register all your GUI components that you want to listen for clicks with a mouse listener. It is not enough to register the JPanel of the JFrame with a MouseListener. So if you only have registered an ActionListener for your buttons, you also have to add a mouse listener.
I have created a quick solution that works for me. It only shows that it works. I have not tried to make the solution generic with many different GUI objects. But that should be quite easy to refactor in when you have got the basics to work.
In the Swing class:
private class MouseListener extends MouseInputAdapter {
public void mouseClicked(MouseEvent e) {}
}
In the init method of the Swing class:
MouseListener myListener = new MouseListener();
btn1.addMouseListener(myListener);
btn2.addMouseListener(myListener);
In the Aspect class:
#Pointcut("execution(* *.mouseClicked(*)) && args(mouseEvent)")
public void mouseEventPointcut(MouseEvent mouseEvent) {}
#Before("mouseEventPointcut(mouseEvent)")
public void beforeMouseEventPointcut(MouseEvent mouseEvent) {
if (mouseEvent.getSource() instanceof JButton) {
JButton clickedButton = (JButton) mouseEvent.getSource();
System.out.println("aspectJ --> mouseClicked: " + clickedButton.getText());
}
}
This results in the following output in the console:
aspectJ --> mouseClicked: Test1
I hope it helps!