I have an <Entry> in xaml, and I want to get that value the user types.
<Entry x:name="enteredInput>
The file with that <Entry> is in startingPage.xaml with a code behind class startingPage.xaml.cs.
Then I would like to transfer that value in the <Label> element of a different xaml, MainPage.xaml.
In your second page, add another constructor with string parameter.
For ex, If your page name is StartingPage.xaml, then add another constructor like below. Inside, assign the incoming value to your label.
public StartingPage(string entryTextFromStartingPage)
{
InitializeComponent();
lblEntryTextDisplay.Text = entryTextFromStartingPage;
}
From the StartingPage.xaml.cs, add the below code in a button click or any event that you are calling the Main page,
Navigation.PushAsync(new MainPage(enteredInput.Text);
Related
I have a CarouselView with ItemTemplate containing a Label and a Grid. Outside of that CarouselView, I want to make a button to modify Carousel's current item's Grid's visibility. Because it's inside ItemTemplate, I can't use x:Name to refer to that specific Grid, so how can I refer to the current item's Grid so I can change its property value? Thank you.
You will want to do that through databinding. As you already mentioned, you can't use x:Name. This is because you're inside of a template. The value in x:Name would be duplicated for each time that template is applied to a concrete item in your list, in this case a CarouselView. Moreover; if you use virtualization for that list, a template might not even exist at all at that point in time. All reasons why you can't use x:Name to reference anything inside of a template.
I don't have any info about the code you want to use this with, so I'll make something up.
If the backing collection for your CarouselView is a ObservableCollection<MyItem>, then your CarouselView might look something like this:
<!-- Databinding scope here is MyViewModel -->
<CarouselView ItemsSource="{Binding MyItemsCollection}">
<CarouselView.ItemTemplate>
<!-- Databinding scope here is MyItem -->
<DataTemplate>
<Button Text="Delete" IsVisible="{Binding CanDelete}" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
So you will have a backing view model which has a MyItemsCollection, and your page (that holds the CarouselView) has set the BindingContext to a new instance of MyViewModel:
public class MyViewModel
{
public ObservableCollection<MyItem> MyItemsCollection { get; set; } = new ObservableCollection<MyItem>();
private void LoadData()
{
var items = _myItemService.GetItems();
foreach (var item in items)
{
MyItemsCollection.Add(item);
}
}
}
Whenever you want to influence the IsVisible you will want to set the CanDelete of the MyItem that it's about to false. Let's assume MyItem looks like this:
public class MyItem
{
public bool CanDelete { get; set; }
}
You will need to implement the INotifyPropertyChanged interface on it so that the UI will pick up on any changes that are made to property values.
Now whenever you set the CanDelete of a certain instance of MyItem to false, that will change your UI. E.g.: MyItemsCollection[3].CanDelete = false;
On my YouTube channel I added a playlist with videos about data binding which might help in cases like these.
PS. At the time of writing IsVisible is bugged in .NET MAUI
I have a popup page in my application which includes some checkboxes, and I also have a ContentPage in my MainPage which contains some stacklayouts, I want to bind the IsVisible Property of my stacklayout to the IsChecked property of my checkbox (which is in another page), but i don't know how to do it, can anybody please help?
I've tried this, but it doesn't work
here's the code in my popup page:
<StackLayout Orientation="Horizontal" Spacing="60">
<local:Checkbox x:Name="va1Checkbox" Text="VA1"/>
<local:Checkbox x:Name="va2Checkbox" Text="VA2"/>
</StackLayout>
and this is the piece of code i have in my MainPage:
<StackLayout IsVisible="{Binding Source={x:Reference
va1Checkbox},Path=IsChecked}">
</StackLayout>
Thanks in advance
<StackLayout Orientation="Horizontal" Spacing="60">
<local:Checkbox x:Name="va1Checkbox" Text="VA1"/>
<local:Checkbox x:Name="va2Checkbox" Text="VA2"/>
</StackLayout>
I believe your popup represents some kind of settings based on which you control your StackLayout in Main Page.
If this is the case, there are two possible solutions.
Create a separate class which is binding for both checkbox and layout. You can use this either with MVVM or without it.
Value Passing: open popup from Main Page and register to its close event. When popup is closed you can use its checkbox's value to enable/disable the layout.
In your Popup.xaml.cs :
create an event action
public event Action<Popup> OnClose;
I believe you will have a way to close it. I don't know you are closing it so I will just use OnBackButtonPressed() here:
protected override bool OnBackButtonPressed()
{
OnClose?.Invoke(this);
return base.OnBackButtonPressed();
}
In your MainPage.xaml.cs :
private void OpenPopup()
{
var popup = new PopupPage();
popup.OnClose += OnPopupClosed;
}
void OnPopupClosed(Popup popup)
{
yourStackLayout.IsVisible = popup.va1Checkbox.Value;
}
UPDATE:
You can pass custom data class instead of popup object:
public class PopupData
{
public bool va1CheckboxValue;
public bool va1CheckboxValue;
// other data which you need to access in other page.
}
then
public event Action<PopupData> OnClose;
and
protected override bool OnBackButtonPressed()
{
PopupData data = new PopupData() { va1CheckboxValue = va1Checkbox.Value; }
OnClose?.Invoke(data);
return base.OnBackButtonPressed();
}
and in main page:
void OnPopupClosed(PopupData data)
{
yourStackLayout.IsVisible = data.va1CheckboxValue;
}
Hope this helps.
I don't know very well XAML and MVVM, but I think you can pass MainPage's ViewModel to Popup... if you change MainPage's ViewModel properties in Popup I think these changes will reflect also to MainPage's binded controls.
If you are not using MVVM (you should...), I think you have to pass a reference to properties used in MainPage to Popup... in this way, Popup can change MainPage's properties and its UI could change
I have a RunBaseReport which contains overrided dialog method where I'm adding couple of my controls. One of those controls is a combobox.
Controls enabled() property should be changed when I'm modifying combobox.
So basically I need to know when the value of my dfReportType dialog field changes.
public Object dialog(Object dialog)
{
DialogRunbase dialog = dialog;
;
//adding my combobox
dfReportType = dialog.addFieldValue(typeid(ReportType), ReportType:DefaultType);
//adding some other controls here
return dialog;
}
According to many articles that I found I need to override dialogPostRun Method and do something like this:
public void dialogPostRun(DialogRunbase dialog)
{
super(dialog);
dialog.dialogForm().formRun().controlMethodOverload(true);
dialog.dialogForm().formRun().controlMethodOverloadObject(this);
}
But unfortunately I don't have this method in RunBaseReport class.
Which should be there according to msdn .
Are there any other workarounds?
I'm currently on AX 2012 but I still looked at it. I have the method available in the context menu, but not on the first column. I have to go over "Plus..." to find the method in the second column.
Well, there is no dialogPostRun method in Report object that inherits RunBaseReport, but we have this method in Class that inherits RunBaseReport.
So that was my mistake. I used report object instead of class.
If you want to make custom dialog for the report but you also want to use all default controls you should:
Create class
Inherit RunBaseReport
Override dialog, getFromDialog etc.
Override lastValueElementName method
public identifiername lastValueElementName()
{
//just put name of your report object
return reportStr(YourReportName);
}
Don't forget to add main() method if you going to make call from menuItem.
I am using the convention-based binding from Caliburn.Micro, but I have a small issue:
How do I set the property that my binding should bind to? If I create a control with x:Name="SomeProperty", how do I choose if the value of SomeProperty should be binded to the Value Property of my control, the OnClick Event of my control or something different, like the Content or the Tag property?
Example: I have this HyperlinkButton that I want to bind to a specific URL, and I want to bind the OnClick to an event handler in my ViewModel.
<HyperlinkButton x:Name="BookDetailsViewModel_InfoLink" Content="Read more" />
The Content property however is not filled with Read more but with the value of the URL. In this example, how do I:
Set the navigation URI to the value of the URL in my ViewModel property
Set the content to "Read more"
Specify an event handler in my ViewModel that will handle the click
Can anyone help me please?
You can customise the ConventionManager per element type in CM. The default out-of-box implemententation is applied to any element which doesn't have an explicit customisation
To add a new convention you just simply call ConventionManager.AddElementConvention
The method looks like this (from CM source)
/// <summary>
/// Adds an element convention.
/// </summary>
/// <typeparam name="T">The type of element.</typeparam>
/// <param name="bindableProperty">The default property for binding conventions.</param>
/// <param name="parameterProperty">The default property for action parameters.</param>
/// <param name="eventName">The default event to trigger actions.</param>
public static ElementConvention AddElementConvention<T>(DependencyProperty bindableProperty, string parameterProperty, string eventName)
{
return AddElementConvention(new ElementConvention
{
ElementType = typeof(T),
GetBindableProperty = element => bindableProperty,
ParameterProperty = parameterProperty,
CreateTrigger = () => new EventTrigger { EventName = eventName }
});
}
As you can see, it takes a few args - you need to pass the default property for bindings, actions, and triggers e.g.
ConventionManager.AddElementConvention<HyperlinkButton>(HyperlinkButton.NavigateUri, "NavigateUri", "Click");
(assuming the click event is called Click)
Since you aren't binding the Content property any more (because the convention is to now bind NavigateUri) you can just leave that as-is and it should remain 'Read more...'
So now you have a HyperlinkButton control which should bind by convention to the NavigateUri, and call the method which shares it's name when the Click event is triggered.
Edit:
I might make a clarification that I don't think you can bind to both a method and a property on the same VM since you can't have a method and a property that share the same name, but I'm sure CM would bubble the action message up the VM hierarchy if you didn't have the appropriate method on the VM... not tried it though. To bind the actions see my other edit below
Don't forget, you could always just use the explicit syntax for all of this!
<HyperlinkButton Content="Read more..." NavigationURI="{Binding SomeUri}" cal:Message.Attach="[Event Click] = [Action HyperlinkClicked($this.NavigateUri)" />
but it's probably better to go the convention route :)
Edit:
Might add how to get convention to grab the property value from the hyperlink -
<HyperlinkButton x:Name="SomeLink" Content="Read more..." cal:Message.Attach="HyperlinkClicked(SomeLink)" />
CM knows that since you set NavigateUri as the default action parameter, it should grab this and pass it to the method that you specified in the action binding. I'm wondering if $this will also work (you probably would need $this.NavigateUri). You can do this across controls e.g.
<TextBox x:Name="SomeTextBox" />
<HyperlinkButton x:Name="SomeLink" Content="Read more..." cal:Message.Attach="HyperlinkClicked(SomeTextBox)" />
The above would pass the Text property of the textbox to the HyperlinkClicked method by default.
My generic question is as the title states, is it best to load data during ViewModel construction or afterward through some Loaded event handling?
I'm guessing the answer is after construction via some Loaded event handling, but I'm wondering how that is most cleanly coordinated between ViewModel and View?
Here's more details about my situation and the particular problem I'm trying to solve:
I am using the MVVM Light framework as well as Unity for DI. I have some nested Views, each bound to a corresponding ViewModel. The ViewModels are bound to each View's root control DataContext via the ViewModelLocator idea that Laurent Bugnion has put into MVVM Light. This allows for finding ViewModels via a static resource and for controlling the lifetime of ViewModels via a Dependency Injection framework, in this case Unity. It also allows for Expression Blend to see everything in regard to ViewModels and how to bind them.
So anyway, I've got a parent View that has a ComboBox databound to an ObservableCollection in its ViewModel. The ComboBox's SelectedItem is also bound (two-way) to a property on the ViewModel. When the selection of the ComboBox changes, this is to trigger updates in other views and subviews. Currently I am accomplishing this via the Messaging system that is found in MVVM Light. This is all working great and as expected when you choose different items in the ComboBox.
However, the ViewModel is getting its data during construction time via a series of initializing method calls. This seems to only be a problem if I want to control what the initial SelectedItem of the ComboBox is. Using MVVM Light's messaging system, I currently have it set up where the setter of the ViewModel's SelectedItem property is the one broadcasting the update and the other interested ViewModels register for the message in their constructors. It appears I am currently trying to set the SelectedItem via the ViewModel at construction time, which hasn't allowed sub-ViewModels to be constructed and register yet.
What would be the cleanest way to coordinate the data load and initial setting of SelectedItem within the ViewModel? I really want to stick with putting as little in the View's code-behind as is reasonable. I think I just need a way for the ViewModel to know when stuff has Loaded and that it can then continue to load the data and finalize the setup phase.
Thanks in advance for your responses.
For events you should use the EventToCommand in MVVM Light Toolkit. Using this you can bind any event of any ui element to relaycommand. Check out his article on EventToCommand at
http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx
Download the sample and have a look. Its great. You won't need any codebehind then. An example is as follows:
<Page x:Class="cubic.cats.Wpf.Views.SplashScreenView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="SplashScreenPage">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding LoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<Label Content="This is test page" />
</Grid>
</Page>
and the view mode could be like this
public class SplashScreenViewModel : ViewModelBase
{
public RelayCommand LoadedCommand
{
get;
private set;
}
/// <summary>
/// Initializes a new instance of the SplashScreenViewModel class.
/// </summary>
public SplashScreenViewModel()
{
LoadedCommand = new RelayCommand(() =>
{
string a = "put a break point here to see that it gets called after the view as been loaded";
});
}
}
if you would like the view model to have the EventArgs, you can simple set PassEventArgsToCommand to true:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding LoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
and the view model will be like
public class SplashScreenViewModel : ViewModelBase
{
public RelayCommand<MouseEventArgs> LoadedCommand
{
get;
private set;
}
/// <summary>
/// Initializes a new instance of the SplashScreenViewModel class.
/// </summary>
public SplashScreenViewModel()
{
LoadedCommand = new RelayCommand<MouseEventArgs>(e =>
{
var a = e.WhateverParameters....;
});
}
}
The following solution is similar to the one already provided and accepted, but it does not use a command in the view model to load the data, but a "normal method".
I think commands are more suited for user actions (commands can be available and not available at runtime), that is why a use a regular method call, but also by setting a an interaction trigger in the view.
I suggest this:
Create a view model class.
Instantiate the view model class within the xaml of the view by creating it inside the DataContext property.
Implement a method to load the data in your view model, e.g. LoadData.
Set up the view, so that this method is called when the view loads.
This is done by an interaction trigger in your view which is linked to the method in the view model (references to "Microsoft.Expression.Interactions" and "System.Windows.Interactivity" are needed):
View (xaml):
<Window x:Class="MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test"
xmlns:viewModel="clr-namespace:ViewModels"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
>
<Window.DataContext>
<viewModel:ExampleViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<ei:CallMethodAction TargetObject="{Binding}" MethodName="LoadData"/>
</i:EventTrigger>
</i:Interaction.Triggers>
This will call the LoadData method in the ViewModel at runtime when the view is loaded. This is where you load your data.
public class ExampleViewModel
{
/// <summary>
/// Constructor.
/// </summary>
public ExampleViewModel()
{
// Do NOT do complex stuff here
}
public void LoadData()
{
// Make a call to the repository class here
// to set properties of your view model
}
If the method in the repository is an async method, you can make the LoadData method async too, but this is not needed in each case.
By the way, generally I would not load data in the constructor of the view model.
In the example above the (parameter less) constructor of the view model is called when the designer shows your view. Doing complex things here can cause errors in the designer when showing your view (for the same reason I would not make complex things in the views constructor).
In some scenarios code in the view models constructor can even cause issues at runtime, when the view models constructors executes, set properties of the view model which are bound to elements in the view, while the view object is not completely finished creating.
Ok, then. :-)
You can bind to a method in the ViewModel by using a behavior.
Here is a link that will help you with that.
http://expressionblend.codeplex.com/
I decided to just have the XAML declaratively bound to a Loaded event handler on the View's code-behind, which in turn just called a method on the ViewModel object, via the View's root element UserControl DataContext.
It was a fairly simple, straight forward, and clean solution. I guess I was hoping for a way to bind the Loaded event to the ViewModel object in the same declarative way you can with ICommands in the XAML.
I may have given Klinger the official answer credit, but he posted a comment to my question, and not an answer. So I at least gave him a one-up on his comment.
I had this same problem when dealing with messages between a parent window and a child window. Just change the order in which your view models are created in your ViewModelLocator class. Make sure that all view models which are dependent on a message are created before the view model that sends the message.
For example, in your ViewModelLocator class's constructor:
public ViewModelLocator()
{
if (s_messageReceiverVm == null)
{
s_messageReceiverVm = new MessageReceiverVM();
}
if (s_messageSenderVm == null)
{
s_messageSenderVm = new MessageSenderVM();
}
}