Change UserControl on button click (navigation without reloading entire page?) - c#

I want to navigate between usercontrols by clicking button (I don't want to reload entire page)
I have grind in which i have blank usercontrol
<Grid x:Name="Content" Grid.Row="1" Background="#FF1A7E1F">
<UserControl x:Name="MainConent"/>
</Grid>
and i want programaticly to set this usercontrol to existing one e.g.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MainConent.Equals(new MainControl()); // maincontrol is a usercontrol
}
but sadly this doesn't work .... is there any way to navigate without need to reload entire page?

//Have you even checked the purpose of Equals?
First you have to remove the current content of Grid, then set new content.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Content.Children.Clear();
Content.Children.Add(new MainControl());
}

Related

return to MainWindow after using adding a UserControl with .Child

In MainWindow.xaml.cs I switch to a userControl using .Child like so:
Grid1.Visibility = Visibility.Hidden;
Stage.Child = new UserControlName();
Where Grid1 is the grid where the content sits in the main window, (which should be hidden because there's some content that will peek out from behind the usercontrol), and Stage is a border element where I want the UserControl to fill
<Grid >
<Border x:Name="Stage" Grid.RowSpan="4" Grid.ColumnSpan="2"/>
<Grid x:Name="Grid1" FocusManager.FocusedElement="{Binding ElementName=textBox}" Margin="10,10,10,10" Width="1200" Height="649" >
How can UserControlName be closed or hidden from its own xaml.cs file and the view be returned to the main window while simultaneously returning MainWindow's Grid1 to Visible?
I would implement that using Regions from PRISM (https://msdn.microsoft.com/en-us/library/ff921098(v=pandp.40).aspx). But if you created simple application and you don't want to invest time to learn PRISM you can for example extend constructor of UserControlName class by adding parameter of MainWindow type and invoke on this object some method that would change the view to your desire state.
Example of uglly code:
MainWindow:
private void Button_Click(object sender, RoutedEventArgs e)
{
grid1.Visibility = Visibility.Hidden;
br.Child = new UserControl2(this);
}
public void CloseView()
{
grid1.Visibility = Visibility.Visible;
br.Child = null;
}
UserControl:
private MainWindow window;
public UserControl2(MainWindow window)
{
this.window = window;
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
window.CloseView();
}
Another option is to use (for example) the Mvvm Light toolkit and messaging (https://msdn.microsoft.com/en-us/magazine/jj694937.aspx).
The child view would send a message to get closed, the main window would receive this message and hide the grid.
The advantage is that the main window and the child control are still uncoupled meaning that the child does not need to know anything about the main window.
Take this chance and invest some time in the Mvvm pattern and libraries such as Mvvm light.

Trouble with Binding Item in WP8

I developed a take-note app, used the ItemsNote to save the list of notes, and the ItemModifyNote to save the temporary item when modify.
public ObservableCollection<NoteViewModel> ItemsNote
{
get
{
return _itemsNote;
}
set
{
_itemsNote = value;
NotifyPropertyChanged("ItemsNote");
}
}
public NoteViewModel ItemModifyNote { get; set; }
at Mainpage.xaml ( where I display the ItemsNote binding within a LongListSelector), I insert a "edit" button next to each note, so when I click it, I set the ItemModifyNote's data to selected item in ItemsNote, then navigate to the "modifyNotePage.xaml"
private void btEditNote_Click(object sender, RoutedEventArgs e)
{
var button = (sender as Button).DataContext as NoteViewModel;
if (button != null)
{
int intIndex = App.ViewModel.ItemsNote.IndexOf(button);
string modifyUri = "/Pages/NoteModifyPage.xaml?Id=" + intIndex.ToString();
App.ViewModel.ItemModifyNote = App.ViewModel.ItemsNote.ElementAt(intIndex);
NavigationService.Navigate(new Uri(modifyUri, UriKind.RelativeOrAbsolute));
}
}
at the ModifyNotePage.xaml, I modify the data of ItemModifyNote (which include a title and a content, both are string) by 2 textbox
<TextBox Grid.Column="1"
Text="{Binding ItemModifyNote.NoteTitle, Mode=TwoWay}" x:Name="tbxModifyNoteTitle"
FontFamily="Clear Sans Light" BorderThickness="0.0"
KeyDown="tbxModifyNoteTitle_KeyDown"/>
</Grid>
<TextBox Grid.Row="1" Margin="0,0,0,20"
x:Name="tbxModifyNoteContent" Text="{Binding ItemModifyNote.NoteContent, Mode=TwoWay}"
AcceptsReturn="True" TextWrapping="Wrap" BorderThickness="0.0" FontFamily="Clear Sans Light"
GotFocus="tbxModifyNoteContent_GotFocus" LostFocus="tbxModifyNoteContent_LostFocus"/>
finally I use 2 buttons: Cancel and Save.
In Save button I set the data of item in ItemsNote by the data of ItemModifyNote
private void btCancel_Click(object sender, EventArgs e)
{
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
private void btSave_Click(object sender, EventArgs e)
{
App.ViewModel.ItemsNote[key] = App.ViewModel.ItemModifyNote;
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
the problem is: even when I click the cancel button, the note still save the modify text ???
That's because ItemModifyNote references NoteViewModel instance from ItemsNote. Since both TextBox in edit page and LongListSelector in the main page operating on the same veiwmodel instance, when user modify ItemModifyNote property, LongListSelector will display updated value without any more code needed.
To avoid this behavior, in button edit click event handler method, try to create a new instance of NoteViewModel and copy it's properties value from the one in ItemsNote, instead of directly referencing the existing instance.

How to navigate to other page with button in WPF

I have a second .xaml page set up under the name Page2.xaml and I want to make it so that when my button is clicked, the user is taken to Page2.xaml
I have this for my button inside of my Page1.xaml:
<Grid>
<Button x:Name="localModeBtn"
Style="{StaticResource MainButtonStyle}"
Content="local mode"
Click="localModeBtn_Click" />
</Grid>
And for the button event handler:
private void localModeBtn_Click(object sender, RoutedEventArgs e)
{
Uri uri = new Uri("Page2.xaml", UriKind.Relative);
this.NavigationService.Navigate(uri);
}
Upon clicking the button I receive an error that says "Cannot locate resource page2.xaml"
The thing is that Page2.xaml is in the same folder as Pag1.xaml so I can't see where I've gone wrong?
Solution to my own question:
I feel a bit silly providing a solution to my own question but thanks to Jasti's link I was able to sort my code out. As he had only posted a comment, I can't mark it as an answer, so here is the solution.
I changed the NavigationWindow to a Window and inserted:
<DockPanel>
<Frame x:Name="_NavigationFrame" NavigationUIVisibility="Hidden" />
</DockPanel>
And within the constructor of the MainWindow.xaml.cs I added:
_NavigationFrame.Navigate(new Page1());
Then the last step was to adjust the button event handler to:
this.NavigationService.Navigate(new Uri("Pages/Page2.xaml", UriKind.Relative));
You should use this, this worked for me:
var Page2= new Page2(); //create your new form.
Page2.Show(); //show the new form.
this.Close(); //only if you want to close the current form.
There is a variable type of a page with the page.xaml right name in your solution.
after that, you should use its methods to do it functionally.
Use any container and bind the content to any property in your viewmodel or codebehind.
After that you just have to update the property by setting a new page and call the PropertyChanged-event (see INotifyPropertyChanged interface). This will update the content of your container and you can display anything you want.
In case you want a separate window
NavigationWindow navWIN = new NavigationWindow();
navWIN.Content = new pageWFbchAdmin();
navWIN.Show();
//winBchAdmin.ShowDialog();
You don't need any C# code for this, just do it in XML:
<Button Content="local mode"
Command="NavigationCommands.GoToPage"
CommandParameter="/Page2.xaml"/>
(Reformatted code not tested)
private void Navigate_Click(object sender, RoutedEventArgs e)//By Prince Jain
{
this.NavigationService.Navigate(new Uri("Page3.xaml", UriKind.Relative));
}
My solution was adding a frame in the main window MainWindow.xaml
<Frame Name="Main" Content="" Margin="127,0,0,0" Background="#FFFFEDED" />
For navigation:
1- Navigating from the main windows on button click:
private void Button_Click(object sender, RoutedEventArgs e)
{
// navigate to pages/projects.xaml inside the main frame
Main.Content = new MyProject.Pages.Projects();
}
2- In case of navigation from the page inside a frame ex Projects.xaml
// declare a extension method in a static class (its your choice if you want to reuse)
// name the class PageExtensions (you can choose any name)
namespace MyProject
{
public static class PageExtensions
{
public static void NavigateTo(this Page page, string path)
{
Frame pageFrame = null;
DependencyObject currParent = VisualTreeHelper.GetParent(page);
while (currParent != null && pageFrame == null)
{
pageFrame = currParent as Frame;
currParent = VisualTreeHelper.GetParent(currParent);
}
if (pageFrame != null)
{
pageFrame.Source = new Uri(path, UriKind.Relative);
}
}
}
}
// to navigate from 'pages/projects.xaml' to another page
// heres how to call the extension on button click
this.NavigateTo("NewProject.xaml");
In addition, you can add another extension method that expects another Page object, in case you want to pass parameters to the constructor
// overloading NavigateTo
public static void NavigateTo(this Page page, Page anotherPage)
{
Frame pageFrame = null;
DependencyObject currParent = VisualTreeHelper.GetParent(page);
while (currParent != null && pageFrame == null)
{
pageFrame = currParent as Frame;
currParent = VisualTreeHelper.GetParent(currParent);
}
// Change the page of the frame.
if (pageFrame != null)
{
pageFrame.Navigate(anotherPage);
}
}
// usage
this.NavigateTo(new Pages.EditProject(id));
First of all, you'll need a frame in your window to hold the pages, so in my MainWindow.xaml I'll add a frame like this:
<Frame x:name="mainFrame"/>
Then We'll add an event listener to our navigation button in our MainWindow.xaml like this:
<Button
x:Name="navBtn"
Content="LIVE VIEW"
Click="NavBtn_Click">
</Button>
Now after we've set our window xaml up, we'll go to MainWindow.xaml.cs and write our code:
//this function should be automatically generated
private void NavBtn_Click(object sender, RoutedEventArgs e)
{
//we'll write this line, which opens our page
mainFrame.Content = new YourPage();
}
and that's it your navigation is ready!
In View (.xaml file):
<StackPanel>
<TextBlock>Outside area of frame</TextBlock>
<StackPanel Height="20" Width="400" VerticalAlignment="Top" Orientation="Horizontal">
<Button Content="Page 1" Width="200" Click="Button_Click"/>
<Button Content="Page 2" Width="200" Click="Button_Click_1"/>
</StackPanel>
<Frame Name="Main" Height="300" Width="700" Background="LightGray">
</Frame>
</StackPanel>
In code behind (.xaml.cs file):
private void Button_Click(object sender, RoutedEventArgs e)
{
Main.Content = new Page1();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Main.Content = new Page2();
}
Thesse two buttons will now help you to navigate between pages named Page1 and Page2. (Please take care of namespaces if the pages are present in folders or so).

GoBack button doesn't work if there's parameter passed in

I create a very simple project to test navigation. Below are the steps.
Create a Blank App (XAML/C#) project.
Add a Basic Page "PageTwo" to the project.
Add a HyperlinkButton and a TextBox to the MainPage.
In the code-behind of the MainPage, using Frame.Navigate method to navigate to PageTow and pass the TextBox's Text as the parameter.
Override OnNavigatedTo method of the PageTwo to get the passed parameter.
Run the project, input some text to the TextBox and click button to PageTwo, it works well, but if I click the built-in Back Button from PageTwo, I get an exception: Value cannot be null. If I comment the override OnNavigatedTo method, the Back button can lead me to the Main Page without exception.
Anyone can help?
MainPage.xaml:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBox Width="200" Name="TB"/>
<HyperlinkButton Content="Go to PageTwo" Click="HyperlinkButton_Click_1"/>
</StackPanel>
</Grid>
MainPage.xaml.cs:
private void HyperlinkButton_Click_1(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(PageTwo), TB.Text);
}
PageTwo.xaml:
<TextBlock Name="TB" Grid.Row="1"/>
PageTwo.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
TB.Text = e.Parameter as string;
}
In general when overriding any of the UI methods, you need to also call the base.
Your code does not cause an exception if I change the PageTwo.xaml.cs override of OnNavigatedTo to the following:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
TB.Text = e.Parameter as string;
// call base method
base.OnNavigatedTo(e);
}

How to Programmatically change a Tab Control's Index & State (Perhaps using commands?)

I have a WPF TabControl which contains a number of TabItems with child UserControls, like this.
XAML:
<TabControl x:Name="tabsMain" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Grid.Row="0" Grid.RowSpan="3" Background="lightgray">
<TabItem Width="100" Header="Profile" FontSize="16">
<InfoControl:InfoTab x:Name="myInfo" />
</TabItem>
<TabItem Width="120" x:Name="Summary" Header="Summary" FontSize="16">
<SummaryControl:SummaryTab/>
</TabItem>
</TabControl>
Within one of the UserControls, lets say, InfoTab, I have a Button. When this Button is clicked I would like to change the index of the TabControl to the SummaryTab and select a radio button on the SummaryTab page.
My problem is that the InfoTab user control does not have access to the MainUserControl which contains the TabControl shown above. I figured out a kludge which changes the SelectedIndex of the TabControl, but this is a very ugly solution and I'd prefer to do something more clean. Also I cannot currently change the RadioButton on my SummaryTab.
My Current C# hack:
Private void btnSummaryDetails_Click(object sender, RoutedEventArgs e)
{
TabControl tabControl = UIHelper.FindChild<TabControl>(Application.Current.MainWindow, "tabsMain");
tabControl.SelectedIndex = 7;
}
Is it possible to use commands or dependency properties to select the SummaryTab and my desired RadioButton? I'm still new to the WPF world, and would love to learn more about this. Thanks in advance.
See my post here for the UIHelper definition I use in the C# above.
One thought comes to mind that will not require too many changes.
First, add an event to your InfoTab class:
public event EventHandler SummaryButtonClicked;
Then handle that in your main form by replacing the control declaration with:
<InfoControl:InfoTab x:Name="myInfo" SummaryButtonClicked="summaryButtonClicked" />
And give a name to your SummaryTab:
<SummaryControl:SummaryTab x:Name="summaryTab" />
Then add the event handler in your main form:
void MainWindow_SummaryButtonClicked(object sender, EventArgs e)
{
this.summaryTab.SelectRadioButton();
}
And add a method in your SummaryTab class to select your radio button.
public void SelectRadioButton()
{
// TODO: something like
myRadioButton.IsChecked = true;
}
You could probably use WPF routed events to solve your problem. Routed events use the WPF visual tree to send events up to parent controls (bubbling) or down to child controls (tunneling) without excessive coupling. I've tried to give a simple example below because I know that routed events can be a bit hairy to learn at first but it's well worth it...
In your main window, define a routed event and add a handler method:
public partial class MainWindow : Window {
public static RoutedEvent ClickedEvent = EventManager.RegisterRoutedEvent(
"Clicked",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(MainWindow));
public MainWindow() {
InitializeComponent();
this.AddHandler(MainWindow.ClickedEvent, new RoutedEventHandler(OnClickedEvent));
}
public void OnClickedEvent(object sender, RoutedEventArgs e) {
// do your work here
}
}
In your button click handler, raise the event:
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e) {
// raise the event (gets bubbled up to the parent of the control)
this.RaiseEvent(new RoutedEventArgs(MainWindow.ClickedEvent));
}
}
The next step would be to tunnel another event down the visual tree and let the other usercontrol listen for it.
I ended up adding a public method as Jeremy suggested in his post. A simple but effective solution. Thanks Jeremy!
Another key realization was that in order to switch the tabcontrol by index, I can get a reference to the main user control and set the SelectedItem to the TabItem itself, like this:
Private void btnSummaryDetails_Click(object sender, RoutedEventArgs e)
{
//HACK replace this with a command that toggles to a different tab instead of this tab reference
MainUserControl mainUserControl = UIHelper.FindChild<MainUserControl>(Application.Current.MainWindow, "root");
mainUserControl.tabsMain.SelectedItem = mainUserControl.Summary;
mainUserControl.SummaryUserControl.SelectRadioButton();
}
Then as suggested by Jeremy, my solution was something like:
public void SelectRadioButton()
{
// TODO: something like
myRadioButton.IsChecked = true;
}
My XAML structure was like
// my main user control:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
x:Name="root"
>
<TabControl x:Name="tabsMain" ...>
<TabItem x:Name="Summary" ... />
</TabControl>
</UserControl>
I think Andrew Jackson's comments are absolutely valid - long term, I plan to investigate using routed command or routed events to traverse the visual tree, but for now I'm sticking with this "quick and dirty" solution as we're not shipping this product. Based on my investigation Routed Events might be a little overkill for this situation.

Categories

Resources