How to navigate to previous page in WPF but keep the data? - c#

How do I navigate to the previous page in WPF but keep the data?
Suppose I have 2 pages in my app Page1 and Page2. and let's say that in page 1 the user writes some inputs for example his name, email etc...
After the user filled in his details on page 1 he navigated to page 2.
Now he wants to go back to the same page 1, how can he go back to the same page 1 with all his data?
Until now I navigated between pages by creating a new page like this:
private void PreviousButton(object sender , RoutedEventArgs e)
{
Page1 p1 = new Page1();
NavigationService.Navigate(p1);
}

You can keep a reference of Page1 in Page2:
Page1: Xaml
<Grid>
<StackPanel>
<TextBox x:Name="TxbName" Margin="10"/>
<TextBox x:Name="TxbEmail" Margin="10"/>
<Button x:Name="BtnNext" Content="Next Page" Click="BtnNext_Click"/>
</StackPanel>
</Grid>
Page1 C#
public partial class Page1
{
public Page1()
{
InitializeComponent();
}
private void BtnNext_Click(object sender, RoutedEventArgs e)
{
var page2 = new Page2(this);
NavigationService.Navigate(page2);
}
}
Page2 Xaml:
<Grid>
<StackPanel>
<Label x:Name="LblName" Margin="10"/>
<Label x:Name="LblEmail" Margin="10"/>
<Button x:Name="BtnPrevious" Content="Previous Page" Click="BtnPrevious_Click"/>
</StackPanel>
</Grid>
Page2 C#:
public partial class Page2
{
private Page1 _page1;
public Page2(Page1 page1)
{
InitializeComponent();
_page1 = page1;
}
private void BtnPrevious_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(_page1);
}
}

Try to set KeepLive = true in page attribute. Check whether it works for you.

Related

Unable to access and reuse controls from other page

I'm trying to use a page to access some controls in a different page but for some reason, it's not working despite setting the necessary references to the other page? Any ideas on why this is not working and how this can be fixed?
Expected result:
Open app > MainList > click list item > go to PageSunflower > PageSunflower should show controls of the PageTest.xaml file
Current result:
Error - Object reference not set to an instance of an object
MainList.xaml.cs
public sealed partial class MainList : Page
{
public List<ListItem> listItemMains;
public MainList ()
{
this.InitializeComponent();
listItemMains = ItemManagerMains.GetListItems();
}
private void ListMain_ItemClick(object sender, ItemClickEventArgs e)
{
ListItemMain item = (ListItemMain)e.ClickedItem;
if (item.FlowerName == "Sunflower")
{
Frame.Navigate(typeof(PageSunflower));
}
else
{
Frame.Navigate(typeof(PageDaffodil));
}
}
}
PageSunflower.xaml
<Page [...]>
<Grid>
</Grid>
</Page>
PageSunflower.xaml.cs
public sealed partial class PageSunflower : Page
{
public PageSunflower()
{
this.InitializeComponent();
TabView tabview = PageTest.Current.MyTabs;
TextBlock txtTitle = PageTest.Current.txtPageTitle;
txtTitle.Text = "hello";
}
}
PageTest.xaml
<Page [...]>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock x:Name="txtTitle" x:FieldModifier="public"/>
<controls:TabView x:FieldModifier="public" Grid.Row="1" x:Name="MyTabs"/>
</Grid>
</Page>
PageTest.xaml.cs
public sealed partial class PageTest : Page
{
public PageTest()
{
this.InitializeComponent();
Current = this;
}
public static PageTest Current;
}
If you want to get the control of other page through static variables, you need two conditions:
This page has already been loaded.
The page is cached.
Since the controls on the page are called, the controls on the page will be instantiated only after the page is loaded.
In addition, when the page is not the content of the current Frame, the page may be unloaded, and the reference will be released at this time, the control cannot be obtained, so the page should be cached.
So if you want to get the controls in a page, please make sure that the page has been loaded (that is, once navigated to the page), and then cache the page, like this:
public PageTest()
{
this.InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
Current = this;
}
Thanks.

WPF UserControl- pre-Load UserControl

I have a MainWindow and 4 UserControls. By switching the DataContext to my UserControls I can have an application with multiple "Pages". In every UserControl I have a webBrowser-Control that Displays an PowerPoint (so -> 4 UC = 4 ppt). The issue I have now is that when I Switch my DataContext (Switch Page) I have to load (call the navigate Method) the whole ppt in my webBrowser again and that takes some Time. How can I fix this?
thanks in advance :))
Adrian
EDIT CODE
MainWindow.xaml
<Window.Resources>
<DataTemplate x:Name="Page1Template" DataType="{x:Type viewmodels:Page1Model}" >
<views:Page1 DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Page2Template" DataType="{x:Type viewmodels:Page2Model}">
<views:Page2 DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Page3Template" DataType="{x:Type viewmodels:Page3Model}">
<views:Page3 DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Page4Template" DataType="{x:Type viewmodels:Page4Model}">
<views:Page4 DataContext="{Binding}"/>
</Window.Resources>
// ...
<ContentControl Content="{Binding}"></ContentControl>
MainWindow.xaml.cs (i call the page Switch like this)
private void menuBtn1_Click(object sender, RoutedEventArgs e)
{
DataContext = new Page1Model();
}
private void menuBtn2_Click(object sender, RoutedEventArgs e)
{
DataContext = new Page2Model();
}
private void menuBtn3_Click(object sender, RoutedEventArgs e)
{
DataContext = new Page3Model();
}
private void menuBtn4_Click(object sender, RoutedEventArgs e)
{
DataContext = new Page4Model();
}
and lets say e.g my UserControl1: (when i call UC1 every time the ppt is opening again, i want just to open it one time):
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
powerPointBrowser1.Navigate("somePPTfile.pptx");
powerPointBrowser1.LoadCompleted += powerPointBrowser1_LoadCompleted;
}
i hope i made it clear :S
In Mainwindow.xaml Place the view in stackpanel to make visiblity hide show like this.
<DataTemplate x:Name="Page1Template" DataType="{x:Type viewmodels:Page1Model}">
<StackPanel Visibility="{Binding Page1}">
<views:Page1 DataContext="{Binding}"/>
</StackPanel>
</DataTemplate>
In MainWindow.xaml.cs you have to set a property for visibility like this.
private Visibility page1;
public Visibility Page1
{
get { return page1; }
set { page1 = value; }
}
Then initialize the DataContext of each view in your MainWindowLoad function, so that it will be preloaded. After that you can set the visibility for the each view in your each menu click function like this:
Page1 = Visibility.Visible; or Page1 = Visibility.Collapsed;
I hope it will works.

WPF | Attach a frame to more page

I've got MainWindow in my WPF project, into this class there is a Frame.
<Window x:Class="Gestionale.MainWindow>
<Grid>
<Frame x:Name="frameChanger"/>
<Button x:Name="Prenotation"/>
</Grid>
</Window>
When I click the button Add the frame shows the page Prenotation with this function:
public partial class MainWindow : Window
{
private void Add_Click(object sender, RoutedEventArgs e)
{
enabled_button();
camereButton.IsEnabled = false;
try
{
frameChanger.Navigate(new framePrenotation());
}
catch(Exception ex) { MessageBox.Show("Error!"); }
}
}
The page prenotation has other 3 button:
<Page x:Name="Gestionale.AddPrenotation">
<Grid>
<Button x:Name="DeletePrenotation"/>
<Button x:Name="AlterPrenotation"/>
<Button x:Name="AddPrenotation"/>
</Grid>
</Page>
I need the frameChanger references to AddPrenotation page when I click on "AddPrenotation". How can I do it?
Any guidance will be appreciated
Thanks, Davide
try to write a constructor with parameter into the Page
frameChanger.Navigate(new framePrenotation(frameChanger));`
...
public partial class framePrenotation: Page
{
private Frame frameParent;
public framePrenotation(Frame frame)
{
this.frameParent= frame;
...
}
...
}
but I don't see why you should do something like that...

Binding in XAML AND C# to get the elements of Array in Buttons

I was trying to implement Buttons in XAML in which the Content of the button has to come from an array from my C# code.
This is part of my C# code which has an integer array [,] arr
public sealed partial class MainPage : Page
{
public MainPage()
{
this.NavigationCacheMode = NavigationCacheMode.Required;
}
public int [,] arr; //This array gets values from another C# file in the Main method
private void Click_Me1(object sender, RoutedEventArgs e)
{
Button obj = (Button)sender;
//some code
}
private void Click_Me2(object sender, RoutedEventArgs e)
{
Button obj = (Button)sender;
// some code
}
}
This is part of my XAML code where I have 2 buttons-
<Button x:Name="Click_Me1"
Content="4"
Click="Start"
HorizontalAlignment="Left"
Margin="123,45,0,0"
VerticalAlignment="Top" />
<Button x:Name="Click_Me2"
Content="5"
Click="Start"
HorizontalAlignment="Left"
Margin="123,45,0,0"
VerticalAlignment="Top" />
In these Buttons I want the contents from the array arr of my C# code.I know XAML is declarative language so we cannot get the values of array directly. So how can I use binding in this?
You should use MVVM pattern.
XAML code:
<ListView ItemsSource="{Binding ButtonContents}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding }"></Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel code:
class MainViewModel : ViewModelBase
{
public MainViewModel()
{
ButtonContents = new ObservableCollection<string>();
ButtonContents.Add("3");
ButtonContents.Add("4");
}
public ObservableCollection<string> ButtonContents { get; set; }
}

How to get lable name in dynamically?

<Grid x:Name="LayoutRoot">
<TextBox x:Name="txt_diplay_1" HorizontalAlignment="Left" Height="42" Margin="155,78,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="103.5" GotFocus="txt_diplay_1_GotFocus" />
<TextBox x:Name="txt_diplay_2" Height="42" Margin="297,78,239.5,0" TextWrapping="Wrap" VerticalAlignment="Top" GotFocus="txt_diplay_2_GotFocus" />
<Button x:Name="btn_a" Content="A" HorizontalAlignment="Left" Height="40" Margin="155,147,0,0" VerticalAlignment="Top" Width="73" Click="btn_a_Click" />
<Button x:Name="btn_b" Content="B" Height="40" Margin="237,147,0,0" VerticalAlignment="Top" Click="btn_b_Click" HorizontalAlignment="Left" Width="73" />
<Button x:Name="btn_c" Height="40" Margin="0,147,239.5,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="73" Click="btn_c_Click" >
<Grid Height="30.833" Width="61.5">
<Label x:Name="lbl_1" Content="1" Margin="22.498,6.5,19.501,2.166"/>
<Label x:Name="lbl_2" Content="!" HorizontalAlignment="Right" Margin="0,-4.422,0,13.088" Width="19.501"/>
</Grid>
</Button>
</Grid>
The design will be like this
public partial class MainWindow : Window
{
Control TexboxDetails = null;
Control ButtonDetails;
Button BehaveButton;
public MainWindow()
{
this.InitializeComponent();
}
private void btn_a_Click(object sender, RoutedEventArgs e)
{
ButtonDetails = (Control)sender;
all_in_one();
}
private void btn_b_Click(object sender, RoutedEventArgs e)
{
ButtonDetails = (Control)sender;
all_in_one();
}
private void btn_c_Click(object sender, RoutedEventArgs e)
{
}
private void txt_diplay_1_GotFocus(object sender, RoutedEventArgs e)
{
TexboxDetails = (Control)sender;
}
private void txt_diplay_2_GotFocus(object sender, RoutedEventArgs e)
{
TexboxDetails = (Control)sender;
}
public void all_in_one()
{
BehaveButton = ButtonDetails as Button;
if (TexboxDetails != null)
{
TextBox BehaveTextbox = TexboxDetails as TextBox;
var caret_index = BehaveTextbox.CaretIndex;
BehaveTextbox.Text = BehaveTextbox.Text.Insert(caret_index, BehaveButton.Content.ToString());
BehaveTextbox.Focus();
BehaveTextbox.CaretIndex = caret_index + 1;
}
}
}
With above code i can get Button name dynamically when i click that button.
In above figure one button(btn_c) has two labels. now i want get that separate labels name dynamcially when i click button(btn_c).
You can get them like this (inside the btn_c click handler):
var btn_c = (Button)sender;
Grid grid = (Grid)btn_c.Content;
Label label1 = (Label)grid.Children[0];
string name1 = label1.Name;
Your whole design could really use some rework. Take a look at this code: (notice the reduced number of event handlers; you'll need to modify the XAML to use these)
public partial class MainWindow : Window
{
TextBox LastFocusedTextBox;
public MainWindow()
{
this.InitializeComponent();
}
private void btn_Click(object sender, RoutedEventArgs e)
{
InsertButtonContent((Button)sender);
}
private void txt_diplay_GotFocus(object sender, RoutedEventArgs e)
{
LastFocusedTextBox = (TextBox)sender;
}
public void InsertButtonContent(Button button)
{
if (LastFocusedTextBox != null)
{
string buttonContentString = button.Content as string;
if (string.IsNullOrEmpty(buttonContentString))
{
var grid = button.Content as Grid;
if (grid != null)
buttonContentString = string.Join("", grid.Children.OfType<ContentControl>().Select(x => x.Content));
}
var caret_index = LastFocusedTextBox.CaretIndex;
LastFocusedTextBox.Text = LastFocusedTextBox.Text.Insert(caret_index, buttonContentString);
LastFocusedTextBox.Focus();
LastFocusedTextBox.CaretIndex = caret_index + buttonContentString.Length;
}
}
}
Notice how the Button are passed to the method instead of being stored in a field. Also, unnecessary fields, both in the class and local to the all_in_one() method were removed. To get the contents of the labels in the Grid (e.g. "1!" - I assume this is what you were after, since nothing else could go into a simple string field, and also match the general pattern of your first two buttons), we simply select their contents and join them into a single string, after checking if the content was a string or a Grid.

Categories

Resources