I have mainPage.xaml and userControl.xaml.I called userControl in mainPage. mainPage has a Grid named "grd" and userControl has a button named "btn". Now when I will click on button then click event of userControl will be raised. In this event, I want to hide the Grid(that in mainPage.xaml). How can I access mainPage controls in userControls ?
For windows Phone 8 try this:
(((Application.Current as App).RootVisual as PhoneApplicationFrame).Content as Page)
You can access currently displayed Page using this code :
var mainPage = (PhoneApplicationPage)((App)Application.Current).RootFrame.Content;
Check out my answer, in that OP wanted to set visibility of appbar, my answer will work for Grid also.
how to programatically open and close bottomappbar using a eventhandler that exist in a usercontrol back code?
If you are using MVVM (as you stated in your comment to #har07's answer) you should not hide the grid in the main page using an event handler in your user control page. Instead you should bind a command to the button in the control page. This command should change the view model of the main page and this change should be notified by that view model to the main page.
In the next example I use MVVM Light but other MVVM libraries probably work almost the same.
Add a property to the view model of your main page:
public class MainViewModel : BaseViewModel
{
…your code here
private bool _isValid;
public bool IsValid
{
get
{
return _isValid;
}
set
{
_isValid = value;
RaisePropertyChange("IsValid");
}
}
}
In the main page bind Visibility to IsValid.
<Grid Visibility="{Binding IsValid, Converter={StaticResource converter}}">
…content here
</Grid>
Now, the grid is visible if IsValid is true.
Add a command to the view model of the user control:
public class UserControlViewModel : BaseViewModel
{
…your code here
public RelayCommand InvalidateGridCommand { get; private set; }
public UserControlViewModel()
{
InvalidateGridCommand = new RelayCommand( () => InvalidateGrid() );
}
private void InvalidateGrid()
{
var mainvm = SimpleIoc.Default.GetInstance(MainViewModel);
mainvm.IsValid = false;
}
}
In the user control page bind the Button to the Command:
<Button Command="{Binding InvalidateGridCommand}">
Invalidate
</Button>
Now, clicking the button will set IsValid on the MainViewModel to false, which will, in turn, hide the Grid.
I came here looking for an answer for this question and after some tries I could access the main page of my Windows Phone 8.1 app using:
var mainPage = (MainPage)(Window.Current.Content as Frame).Content.
To access the controls declared in the MainPage, you need also to give them a x:Name and change their x:FieldModifier to "Internal" or "Public".
Related
I am new to WPF and MVVM (coming in from WinForms and Events), so please bear with me!
I am trying to figure out how to use the same INotifyPropertyChanged value binding between multiple views. I am using MVVM Light. I have ViewModels that inherit from ViewModelBase backing my Views (with no code behind). I'm not sure how to explain the issue, but I think an example will make it clear what I'm after.
I have a main window. It has a standard TabControl. On the Login TabItem I have a custom login control. Below the TabControl, I have a custom status bar control. The desired behavior is that when the user logs in, the status bar is updated with their login status and name, and the other TabItems on the main window become enabled (they should be disabled when not logged in).
So to summarize I have:
MainWindow (view) with MainWindowViewModel
Login (view) with LoginViewModel (in TabControl of MainWindow)
StatusBar (view) with StatusBarViewModel (at bottom of MainWindow)
Here is what my StatusBarViewModel looks like:
public class StatusBarViewModel : ViewModelBase, IStatusBarViewModel
{
private bool _isLoggedIn;
public bool IsLoggedIn
{
get { return _isLoggedIn; }
set { Set(ref _isLoggedIn, value); RaisePropertyChanged(); }
}
// other properties follow
}
I inject (using Ninject) the (singleton) concrete instance of IStatusBarViewModel into the LoginViewModel via constructor injection:
public class LoginViewModel : ViewModelBase
{
private IStatusBarViewModel _statusBar;
public LoginViewModel(IStatusBarViewModel statusBar)
{
_statusBar = statusBar;
}
}
And I do the same for the MainWindowViewModel:
public class MainWindowViewModel : ViewModelBase
{
private IStatusBarViewModel _statusBar;
public bool IsLoggedIn => _statusBar.IsLoggedIn;
public MainWindowViewModel(IStatusBarViewModel statusBar)
{
_statusBar = statusBar;
}
}
Note: I think this is where my problem is... Not sure if MVVM Light interprets this as a bindable property and applies the proper change notifications. If I add a setter (which I don't need here), that won't work because A property or indexer may not be passed as an out or ref parameter. So I'm unclear on what is going on when I do this.
Back on track here, so when the login is successful, I am able to update the IsLoggedIn property from the LoginViewModel like so:
_statusBar.IsLoggedIn = true;
I set up the binding in my MainWindow XAML like so:
<TabItem Header="Event" IsEnabled="{Binding IsLoggedIn}">
<views:Events/>
</TabItem>
The binding works correctly when the view is first loaded, but subsequent changes to the property don't trigger a change in IsEnabled. The StatusBar (view) however does update accordingly.
I had tossed around the idea of injecting references to both the StatusBarViewModel and the MainWindowViewModel in to my LoginViewModel (and then having to set two properties after login), but that made me think that I'm not approaching this correctly because I'm creating dependencies.
So basically the question is:
Is my approach correct, per the MVVM pattern?
Am I on the right track and just need to modify the code a bit?
If not, what is the (or a) standard pattern to handle this scenario?
Your guess is correct. The problem is here:
public bool IsLoggedIn => _statusBar.IsLoggedIn;
... because it's not going to generate the change notification. What you could do is just expose the IStatusBarViewModel via a public property and then bind to its own IsLoggedIn property directly.
In the ViewModel:
public class MainWindowViewModel : ViewModelBase
{
private IStatusBarViewModel _statusBar;
public IStatusBarViewModel StatusBar => _statusBar;
public MainWindowViewModel(IStatusBarViewModel statusBar)
{
_statusBar = statusBar;
}
}
And in the View:
<TabItem Header="Event" IsEnabled="{Binding StatusBar.IsLoggedIn}">
<views:Events/>
</TabItem>
I am building a WPF application with the aid of MVVM light and Unity.
I have a ribbon control in the main form, with one tab without tab category, and two tabs in a tab category. User control 3 includes user control 2 and user control 1.
I would like to make visible the Tab Category and tabs 2 and 3 when I click in user control 2 and make them invisible when click in user control 3 or 1.
I confused a little bit with the event handling in mvvm structure.
I would like to do this properly with the MVVM rules. Could you give me an example or some really helpful directions how to do it?
Check how it looks like
Your Ribbon or tabs for that matter, should be backed up by a ViewModel, let's say TabCategoryViewModel or Tab2ViewModel and Tab3ViewModel. Inside these ViewModels you inject the IMessanger service (of course register it before if not already done) and create a POCO event message, like SelectedViewMessage
public class SelectedViewMessage
{
public string ViewName { get; set; }
}
Inside your TabCategoryViewModel you would register to listen to this message
public class TabCategoryViewModel : ViewModelBase
{
public readonly IMessanger messageService;
public TabCategoryViewModel(IMessanger messageService)
{
if(messageService == null)
{
throw ArgumentNullException("messageService");
}
this.messageService = messageService;
this.messageService.Register<GoToPageMessage>(this, OnSelectedViewChanged);
}
protected void OnSelectedViewChanged(SelectedViewMessage message)
{
this.IsVisible = message.ViewName == "UserControl2";
}
private bool isVisible;
public bool IsVisible
{
get { return isVisible; }
set
{
if(isVisible != value)
{
isVisible = value;
RaisePropertyChanged();
}
}
}
}
You inject the same IMessanger service into your ViewModel you use to Bind your UserControl2 and fire the message via
var message = new SelectedViewMessage {
ViewName = "UserControl2";
};
this.messangerService.Send<SelectedViewMessage>(message);
This code can be placed inside a ViewSelectedCommand or something similar, and you can use Blend Interactivity Triggers/Actions to bind this to certain events on the View/UserControl
This can be achieved by adding event onPropertyChange whenever user clicks on the tab and add that property in xaml under Visibility tag. Also look at Handling UI Control to understand that mapping in xaml and ti understand event handling from Here
Hope it helps.
I'm using the following code which is copy pasted from the main window which was working as expected ,
I have created View which is user control and put the code of the
code from the main window XAML
In the View model I put reference for the User model
In the user control I put the code for from the the main window which
is related to the event handlers for example the
DropText_PreviewDragEnter & listbox_SelectionChanged
Currently I have 2 issues in the User Control which Im not sure how to overcome...
1. Errors in the user control for all the occurrence of the ListBox (for example from listbox_SelectionChanged ystem.Windows.Controls.ListBox.SelectedItems.Count > 0 . the Selected items are marked at red with the following error
"cannot access non-static property SelectedItems item source in static context". ,not sure what is the reason since in the main window it was the same as static.
2. Since I have copied the code from the main window there is references to user object in the user controlwhich I believe is not acceptable in MVVM ,how should I change it ? for example
var mySelectedItem = System.Windows.Controls.ListBox.SelectedItem as User;
or
bool remove = _UsersList.Remove((User) System.Windows.Controls.ListBox.SelectedItem);
Here is the code.
I will appreciate your help !
The view model
public partial class ModelView : UserControl
{
private const string DRAG_SOURCE = "DragSource";
public ModelView()
{
InitializeComponent();
DataContext = new ModelView();
}
//Drag Over from text box to List box
private void ListBox_PreviewDrop(object sender, DragEventArgs e)
{
object dragSource = e.Data.GetData(DRAG_SOURCE);
if (dragSource != null && dragSource is TextBox)
{
(dragSource as TextBox).Text = String.Empty;
}
if (!String.IsNullOrEmpty(e.Data.GetData(DataFormats.StringFormat).ToString()) && dragSource is TextBox)
{
_UsersList.Add(new User {Name = e.Data.GetData(DataFormats.StringFormat).ToString()});
}
else
{
e.Handled = true;
}
}
}
}
The Xaml is
<TextBox x:Name="name1"
AcceptsReturn="True"
AllowDrop="True"
PreviewDragEnter="DropText_PreviewDragEnter"
PreviewDrop="DropText_PreviewDrop"
PreviewMouseDown="DropText_PreviewMouseDown"
HorizontalAlignment="Left" Height="20" Margin="360,70,0,0" TextWrapping="Wrap" Text=""
VerticalAlignment="Top" Width="70"/>
....
The model view
internal class ModelView
{
private ObservableCollection<User> _UsersList = new ObservableCollection<User>();
public ObservableCollection<User> UserList
{
get { return _UsersList; }
}
public void InitUsers()
{
_UsersList.Add(new User {Name = "fff"});
//Sort the User collection
ICollectionView usersView = CollectionViewSource.GetDefaultView(_UsersList);
usersView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
}
}
You already have two answers explaining why the first issue happend in the previous question. And follwoing points are what #Will said in comment as a mess in MVVM implementation that I can see in your codes :
UsersList in the model view is a Model as in Model-View-ViewModel.
And the model view it self is a ViewModel as in Model-View-ViewModel
Then what you call view model is actually a View in Model-View-ViewModel point of view. It inherits UserControl and UserControl is a view, no difference from Window or Page, etc. They're all View. And even if we agree to call it view model, then it violated MVVM principle everywhere, because view model shouldn't have reference to View/UI control object.
Not directly answering your question, but I hope you get a better prespective on MVVM pattern.
#phil correctly noted that you can't access the ListBox like this:
System.Windows.Controls.ListBox
What he failed to mention is that you shouldn't access a ListBox at all if you're using MVVM. Clearly you're not using MVVM now, but if you want to, then I would recommend that you read up on it so that you can get the full benefit from it. Just having a view and a view model does not mean that you're using MVVM.
In MVVM, we manipulate data, not UI controls. Therefore, you need to create a SelectedItem property in your view model and bind that to the ListBox.SelectedItem property and then you'll always have access to the item that is selected:
public User SelectedItem { get; set; } // Implement INotifyPropertyChanged here
...
<ListBox ItemsSource="{Binding YourCollection}" SelectedItem="{Binding SelectedItem}"/>
Now you can do something with the selected item like this:
string selectedItemName = SelectedItem.Name;
you have to access your listbox by
yourListBoxName.SelectedItems.Count > 0
you can't access it by
System.Windows.Controls.ListBox.SelectedItems.Count
same for
var mySelectedItem = System.Windows.Controls.ListBox.SelectedItem as User;
use the following instead
var mySelectedItem = yourListBoxName.SelectedItem as User;
I am using the MVVM pattern in my project. It uwas using code-behind.
The issue that I'm having is: Included in my project I have a page transition control that I downloaded from Simple WPF Page transitions.
It worked great in code-behind, the xaml as follows:
<Grid ShowGridLines="False">
<pageTransitions:PageTransition Name="pageTransitionControl" Margin="0" TransitionType="GrowAndFade" />
</Grid>
with this in the window tag:
xmlns:pageTransitions="clr-namespace:WpfPageTransitions;assembly=WpfPageTransitions"
In the code-behind I just ran:
mast.Page mp = new mast.Page();
pageTransitionControl.ShowPage(mp);
When I do the below code-behind, it unloads the current page (mp) and loads the new one (dp)
dist.Page dp = new dist.Page();
pageTransitionControl.ShowPage(dp);
Above "mp" and "dp" are new instances of a UserControl (page). pageTransitionControl is the name of the transition control in the xaml.
Now I would like to get it to run via the ViewModel, without communicating with the view as it is doing above, how can I go about this?
Ideally, the PageTransition control would provide a way for you to set the current page via binding. Assuming that it does not provide a way of doing that, then there are a number of ways of achieving this.
Here are three suggestions, in order of "niceness" (in my opinion).
You can create a new page transition control which either is a wrapper for PageTransition or inherits it. And then add a DependecyProperty for the current page to that class which you can bind to, catch the dependecy property change event and call ShowPage.
Write a class inhering FrameworkElement or DependencyObject, depending on usage, which can bind to a page and to the PageTransition control. This class is then responsible for calling ShowPage on the bound PageTransition control when the current page changes.
Bind the PageTransition control to a property on your model and have code in the model access the control via that property.
Example:
public class MyPageTransition : ContentControl
{
public static readonly DependencyProperty CurrentPageProperty =
DependencyProperty.Register(
"CurrentPage",
typeof(object),
typeof(MyPageTransition),
new PropertyMetadata(DependencyPropertyChanged));
public ContentControl()
{
this.Content = this.pageTransition;
}
public object CurrentPage
{
get { return GetValue(CurrentPageProperty); }
set { SetValue(CurrentPageProperty, value); }
}
protected static void DependencyPropertyChanged(
DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (e.Property == CurrentPageProperty)
{
this.pageTransition.ShowPage(CurrentPage);
}
}
private PageTransition pageTransition = new PageTransition();
}
I Have UserControl called "Footer.xaml" and "Header.xaml" Both User Control are place to different window.
Footer.xaml have two button :-
btnBasic
btnStandard
Header.xaml have one button :-
lobby
When i click on Lobby button from the Header.xaml i want to change the IsEnabled property of the both button [ btnBasic and btnStandard ] on my condition.
I Try the below things [ Footer.xaml.cs ] by default the both button IsEnabled = true
public partial class Footer : UserControl
{
static Footer objFooter = new Footer();
public Footer()
{
InitializeComponent();
objFooter = this;
}
public static Footer GetFooterInstance()
{
return objFooter;
}
}
and on Header.xaml.cs
private void btnLobby_Click(object sender, RoutedEventArgs e)
{
Footer objFooter;
objFooter = Footer.GetFooterInstance();
objFooter.btnBasic.IsEnabled = false;
objFooter.btnStandard.IsEnabled = false;
}
But nothings is effect with button.
You tagged your question for MVVM but posted code is completely violating the rules of MVVM here. You can achieve this by stricting to the rules of MVVM in following manner -
Create a ViewModel class which will serve as DataContext for both of your views.
Create a bool property inside it and bind IsEnabled DP for your buttons namely btnBasic and btnStandard with this property.
Create an ICommand in your ViewModel class which will be invoked on lobby button click and will set this bool property to true or false depending on your situation.
But as you posted in comment above, you already have seperate ViewModels for both Views, you can use Event Aggregator to communicate between two ViewModels.