I want to display a small popup in a xamarin forms application (iOS/Android PCL)
I am actually working with a ContentPage (XAML + C# code)
and i am showing this popup with:
await Navigation.PushModalAsync(mypopupinstance)
It works fine, but the popup is on the full screen. I just want a small popup and i want to see what is behind.
Thanks
You'll need to look elsewhere for this kind of functionality. One such library is Rotorgames' Popup plugin: https://github.com/rotorgames/Rg.Plugins.Popup
Modal pages can't be presented like that.
For small popups you can use
DisplayAlert()
Inside a Page.
If you want something more customizable just wrap the content of your Page Inside a relative Layout or grid, and add the popup on top of your normal content.
I am working on the same issue, so far I was able to create a popup which can hold a content page.
I am gladly willing to share my current state. Please note that I will keep the code examples as short as possible, therefore reducing it to a simple loading and a text prompt dialog.
Approach
After having used the Acr.UserDialogs library for a while, i felt the need of having dialogs I could customize due to my personal requirements. Also I wanted to minimize the necessity of having to rely on plugins.
Ideally such a dialog should be invoked with a simple call, for instance:
Dialogs.ShowLoading();
or
string result = Dialogs.ShowPrompt();
As things are with Xamarin.Forms it is quite obvious that we will require a dependency service implementation for that to work.
Shared Code Library
We create a basic interface "IDialogs.cs":
public interface IDialogs
{
bool IsDialogOpen();
void ShowLoading(LoadingDialog dialog);
void ShowPrompt(PromptDialog dialog);
void HideDialog();
}
Next thing to do is to have a static dialog class, which can be called from every page where a dialog is needed.
"Dialogs.cs":
public static class Dialogs
{
private static IDialogs dialogService = DependencyService.Get<IDialogs>();
public static bool IsDialogOpen()
{
return dialogService.IsDialogOpen();
}
public static void ShowLoading()
{
LoadingDialog dlg = new LoadingDialog();
dialogService.ShowLoading(dlg);
}
public static Task<string> ShowPromptText()
{
TaskCompletionSource<string> dialogCompletion = new TaskCompletionSource<string>();
PromptDialog dialog = new PromptDialog();
dialog.Canceled += (object sender, object result) => { dialogService.HideDialog(); dialogCompletion.SetResult((string)result); };
dialog.Confirmed += (object sender, object result) => { dialogService.HideDialog(); dialogCompletion.SetResult((string)result); };
dialogService.ShowPrompt(dialog);
return dialogCompletion.Task;
}
public static void HideDialog()
{
dialogService.HideDialog();
}
}
You will notice, that we are using a TaskCompletionSource together with custom event handlers in the ShowPromptText method. This enables us to display the dialog and await the user pressing the okay or cancel button and consuming the returned result.
As of now, the dialogs are as simple as they can get. I use some additional code for styling and themeing, but i will leave that out in order to keep this answer short and simple.
The loading dialog:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Dialogs.LoadingDialog" BackgroundColor="Transparent">
<ContentPage.Content>
<Grid BackgroundColor="#bb000000">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Frame BackgroundColor="Black" CornerRadius="15" Grid.Row="1" x:Name="ContentGrid" Margin="100,0,100,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ActivityIndicator Grid.Row="0" Color="White" IsRunning="True" HorizontalOptions="Center" VerticalOptions="Center"/>
<Label x:Name="LoadingLabel" Text="Loading ..." VerticalOptions="End" HorizontalOptions="Center" Grid.Row="1" TextColor="White" />
</Grid>
</Frame>
</Grid>
</ContentPage.Content>
(no need to post xaml.cs code for this, since there is no interaction with a loading screen)
The prompt dialog
PromptDialog.Xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:dialogs="clr-namespace:BetterUI.Dialogs"
x:Class="MyApp.Dialogs.PromptDialog" BackgroundColor="Transparent">
<ContentPage.Content>
<ScrollView>
<Grid BackgroundColor="#bb000000">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Frame x:Name="ContentGrid" Grid.Row="1" CornerRadius="15" BackgroundColor="White" Margin="50,0,50,0" Padding="0">
<Grid Grid.Row="1" Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid x:Name="HeadingGrid" Padding="10, 5, 10, 5" Margin="-15,0,-15,0" Grid.Row="0" BackgroundColor="Black">
<Label x:Name="HeadingLabel" Text="Enter text" TextColor="White" Margin="20,0,20,0"/>
</Grid>
<Label l x:Name="DescriptionLabel" Text="Enter your text" Grid.Row="1" Margin="15,0,15,0"/>
<Entry x:Name="DialogResultText" Placeholder="Text" PlaceholderColor="LightGray" TextColor="Black" Grid.Row="2" Margin="15,0,15,0"/>
<Grid Grid.Row="3" ColumnSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="CancelButton" Text="Cancel" Clicked="OnCancelClick" Grid.Column="0" CornerRadius="0"/>
<Button x:Name="ConfirmButton" Text="Okay" Clicked="OnOkayClick" Grid.Column="1" CornerRadius="0"/>
</Grid>
</Grid>
</Frame>
</Grid>
</ScrollView>
</ContentPage.Content>
PromptDialog.xaml.cs:
public partial class PromptDialog : ContentPage
{
public event EventHandler<object> Confirmed;
public event EventHandler<object> Canceled;
public PromptDialog()
{
InitializeComponent();
}
private void OnCancelClick(object sender, EventArgs e)
{
Canceled?.Invoke(this, string.Empty);
}
private void OnOkayClick(object sender, EventArgs e)
{
Confirmed?.Invoke(this, DialogResultText.Text);
}
}
Android implementation
First of all we will create the android implementation of our IDialogs interface created in the shared code earlier:
[assembly: Dependency(typeof(DialogService))]
namespace MyApp.Droid.Services
{
/// <summary>
/// Handles displaying dialog items on screen
/// </summary>
public class DialogService : IDialogs
{
private static DialogFragment currentDialog;
/// <summary>
/// returns if a dialog is already open
/// </summary>
/// <returns></returns>
public bool IsDialogOpen()
{
return (currentDialog != null && currentDialog.IsVisible);
}
/// <summary>
/// Initialize Dialog Service with activity
/// </summary>
/// <param name="activity">activity</param>
public static void Init(Activity activity)
{
Activity = activity;
}
public static Activity Activity { get; set; }
/// <summary>
/// Displays a loading dialog
/// </summary>
/// <param name="dialog">Instance of progress dialog (xamarin.forms)</param>
public void ShowLoading(Dialogs.LoadingDialog dialog)
{
if (Activity == null)
return;
DialogFragment frag = dialog.CreateDialogFragment(Activity);
frag.SetStyle(DialogFragmentStyle.NoTitle, Resource.Style.DialogFrame);
frag.Show(Activity.FragmentManager, "dialog");
currentDialog = frag;
}
/// <summary>
/// Displays a prompt dialog
/// </summary>
/// <param name="dialog"></param>
public void ShowPrompt(Dialogs.PromptDialog dialog)
{
if (Activity == null)
return;
DialogFragment frag = dialog.CreateDialogFragment(Activity);
frag.SetStyle(DialogFragmentStyle.NoTitle, Resource.Style.DialogFrame);
frag.Show(Activity.FragmentManager, "dialog");
currentDialog = frag;
}
/// <summary>
/// Hides loading dialog
/// </summary>
public void HideDialog()
{
if (Activity == null)
return;
if (currentDialog != null)
{
currentDialog.Dismiss();
currentDialog = null;
}
}
}
}
Note that you have to set the activity for the dialog service prior to calling the actual methods to show a dialog, so in your MainActivity.cs make sure to call
DialogService.Init(this);
after having initialized Xamarin.Forms.
Finally, here comes some black magic:
Usually, one would implement such a dialog in Android by putting a fragment container into the main layout and throwing a fragment inside. Unfortunately, due to using Xamarin.Forms, such a main layout isn't available by default.
Even though Xamarin.Forms offers a view extension, which allows converting a ContentPage to a fragment (ContentPage.CreateFragment), we won't have any success of using this because it needs a target fragment container to be placed in.
However, android provides something called a DialogFragment, which can be thrown at the screen without having the need of a defined fragment container.
Unfortunately there isn't any out-of-the-box solution for creating a DialogFragment from Xamarin Forms. Good news is that this can be overcome with the power of using System.Reflection(;), so we create our own extension method and some modified versions of internal classes, xamarin.forms uses under the hood. In order to achieve this, I took the code from Xamarin.Forms in Xamarin.Platform.Android and modified it to create a DialogFragment from a ContentPage:
public static class PageExtensions
{
public static DialogFragment CreateDialogFragment(this ContentPage view, Context context)
{
if (!Forms.IsInitialized)
throw new InvalidOperationException("call Forms.Init() before this");
// Get Platform constructor via reflection and call it to create new platform object
Platform platform = (Platform)typeof(Platform).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(Context), typeof(bool) }, null)
?.Invoke(new object[] { context, true });
// Set the page to the platform
if (platform != null)
{
platform.GetType().GetMethod("SetPage", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(platform, new object[] { view });
// Finally get the view group
ViewGroup vg = (Android.Views.ViewGroup)platform.GetType().GetMethod("GetViewGroup", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(platform, null);
return new EmbeddedDialogFragment(vg, platform);
}
return null;
}
public class DefaultApplication : Xamarin.Forms.Application
{
}
class EmbeddedDialogFragment : DialogFragment
{
readonly ViewGroup _content;
readonly Platform _platform;
bool _disposed;
public EmbeddedDialogFragment()
{
}
public EmbeddedDialogFragment(ViewGroup content, Platform platform)
{
_content = content;
_platform = platform;
}
public override global::Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
this.Dialog.Window.SetSoftInputMode(SoftInput.AdjustResize);
return _content;
}
public override void OnDestroy()
{
this.Dialog?.Window.SetSoftInputMode(SoftInput.AdjustPan);
base.OnDestroy();
}
protected override void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
_disposed = true;
if (disposing)
{
(_platform as IDisposable)?.Dispose();
}
base.Dispose(disposing);
}
}
}
iOS implementation
Fortunately, for the iOS implementation no deep diving into Xamarin.Forms code is necessary:
Here is the iOS implementation of DialogService:
[assembly: Dependency(typeof(DialogService))]
namespace BetterUI.iOS.Services
{
public class DialogService : IDialogs
{
private UIViewController currentDialog;
private UIWindow popupWindow = null;
public void HideLoading()
{
if (currentDialog != null)
{
UIApplication.SharedApplication.KeyWindow.RootViewController.DismissModalViewController(false);
currentDialog.Dispose();
currentDialog = null;
}
}
public bool IsDialogOpen()
{
return (currentDialog != null && currentDialog.IsBeingPresented);
}
public void ShowLoading(LoadingDialog dialog)
{
UIViewController dialogController = dialog.CreateViewController();
ShowDialog(dialogController);
currentDialog = dialogController;
}
public void ShowPrompt(PromptDialog dialog)
{
UIViewController dialogController = dialog.CreateViewController();
ShowDialog(dialogController);
currentDialog = dialogController;
}
private void ShowDialog(UIViewController dialogController)
{
var bounds = UIScreen.MainScreen.Bounds;
dialogController.View.Frame = bounds;
UIApplication.SharedApplication.KeyWindow.RootViewController.ModalPresentationStyle = UIModalPresentationStyle.CurrentContext;
UIApplication.SharedApplication.KeyWindow.RootViewController.AddChildViewController(dialogController);
UIApplication.SharedApplication.KeyWindow.RootViewController.View.Opaque = false;
UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.AllowsGroupOpacity = true;
UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.BackgroundColor = new CGColor(Color.White.ToCGColor(), 0.0f);
UIApplication.SharedApplication.KeyWindow.RootViewController.View.BackgroundColor = UIColor.Clear;
UIApplication.SharedApplication.KeyWindow.RootViewController.View.AddSubview(dialogController.View);
dialogController.ModalPresentationStyle = UIModalPresentationStyle.OverCurrentContext;
dialogController.View.Opaque = false;
dialogController.View.BackgroundColor = UIColor.Clear.ColorWithAlpha(0.0f);
}
}
}
Et voila, now whenever we use the calls from the "Approach"-Section of this post, a nice popup dialog containing our custom Xamarin.Forms ContentPage will show up.
It is easier to use a package, like Plugins.Popup to achieve this, without custom renderers, it is impossible to add an Image to the default AlertDialog, which will limit you.
Using Popup Plugin, you just have to add it to your solution, initialize in both iOS and Android:
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Rg.Plugins.Popup.Popup.Init();
global::Xamarin.Forms.Forms.Init ();
LoadApplication (new App ());
return base.FinishedLaunching (app, options);
}
}
Android:
namespace HelloXamarinFormsWorld.Android
{
[Activity(Label = "HelloXamarinFormsWorld", MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Rg.Plugins.Popup.Popup.Init(this, bundle);
Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication (new App ());
}
}
}
Create a Popup Page
<pages:PopupPage.Animation>
<animations:ScaleAnimation
PositionIn="Center"
PositionOut="Center"
ScaleIn="1.2"
ScaleOut="0.8"
DurationIn="400"
DurationOut="300"
EasingIn="SinOut"
EasingOut="SinIn"
HasBackgroundAnimation="True"/>
</pages:PopupPage.Animation>
<!--You can use any elements here which are extended from Xamarin.Forms.View-->
<StackLayout
VerticalOptions="Center"
HorizontalOptions="Center"
Padding="20, 20, 20, 20">
<Label
Text="Test"/>
</StackLayout>
And, to show in your page:
await Navigation.PushPopupAsync(page);
As of March 22, 2022, there is a Popup Included in the Xamarin Community Toolkit. You can find Microsoft's documentation here.
An object instantiation through XAML:
<xct:Popup xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="clr-namespace:Xamarin.CommunityToolkit.UI.Views;assembly=Xamarin.CommunityToolkit"
x:Class="MyProject.SimplePopup">
<StackLayout>
<Label Text="Hello Simple Popup" />
</StackLayout>
</xct:Popup>
An object instantiation through code:
using Xamarin.CommunityToolkit.UI.Views;
// can also specify a type to be returned from the dismissal of the popup
// e.g. var popup = new Popup<string>
var popup = new Popup
{
// Can set the content to any custom layout
Content = new StackLayout
{
Children =
{
new Label
{
Text = "Hello Simple Popup"
}
}
}
};
You can show the popup through the Navigation stack:
using Xamarin.CommunityToolkit.Extensions;
App.Current.MainPage.Navigation.ShowPopup(popup);
Or call it through await App.Current.MainPage.Navigation.ShowPopupAsync(popup);
It also has events you can intercept and handle, like popup.Dismissed.
The popup can even return a value when it is dismissed if you instantiate it with a type specified:
// provide the <T> type string here for the example
var popup = new Popup<string>
{
Content = new StackLayout
{
Children =
{
new Label
{
Text = "Hello Simple Popup"
},
new Button
{
Text = "Dismiss",
// pass the <T> you would like as the result into Dismiss
// in this case it is a string "Dismiss was clicked"
Command = new Command(() => Dismiss("Dismiss was clicked"))
}
}
}
};
var result = await App.Current.MainPage.Navigation.ShowPopupAsync(popup);
Console.WriteLine(result);
Use package named 'Rg.Plugin.Popup'. It will help you.
I would like to create a User Control which can embed free content.
I created a Dependency property for the content :
public sealed partial class MyUserControl : UserControl
{
public Border MyProperty
{
get { return (Border)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(Border), typeof(VisitList), new PropertyMetadata(new Border() { Height=300, Width=300 }));
...
}
So In my MainPage.xaml, I can use it with following code :
<MyUserControl>
<MyUserControl.MyProperty>
<Border x:Name="MyContent" Width="60" Height="60" Background="Pink">
... Whatever ...
</Border>
</MyUserControl.MyProperty>
</MyUserControl>
From this, I can't find what is the XAML syntax in the MyUserControl.xaml for declaring the placeholder that will be substituted by MyContent at runtime.
I tried with :
<UserControl ... >
....
<Grid ...>
<ContentPresenter Content="{TemplateBinding MyProperty}" />
</Grid>
</UserControl>
But, it crashes with message :
An exception of type 'Windows.UI.Xaml.Markup.XamlParseException' occurred in xXx.exe but was not handled in user code
WinRT information: Failed to create a 'Windows.UI.Xaml.DependencyProperty' from the text 'MyProperty'. [Line: 29 Position: 35]
(The Line: 29 Position:35 refers to Content="{TemplateBinding MyProperty}")
It sounds like you're mixing Templated Controls and UserControls. It's a bit complicated, but basically, TemplateBindings work when they're apart of the ContentTemplate of the control, as opposed to the Content itself (which is what I believe is happening here, based on the xaml you have shown).
Try changing your bindings in this way:
<UserControl x:Name="RootControl" ...>
....
<ContentControl Content="{Binding MyProperty, ElementName=RootControl}" />
....
</UserControl>
What this means is that you will need to have your UserControl implement INotifyPropertyChanged in case it needs to respond to changing Content.
I've created a custom control with a dependency property for databinding.
The binded value should then be displayed in a text box.
This binding works properly.
The problem occurs when I implement my custom control. The grid's data context is a simple view model which contains a String property for binding.
If I bind this property to a standard wpf controls text box everything works fine.
If I bind the property to my custom control nothing happens.
After some debugging I found out that SampleText is searched in CustomControl. Of course it doesn't exist there.
Why is my property searched in CustomControl and not taken from the DataContext as it happens in scenario 1.
<Window x:Class="SampleApplicatoin.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:SampleApplication"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.DataContext>
<controls:ViewModel/>
</Grid.DataContext>
<TextBox Text="{Binding SampleText}"/>
<controls:CustomControl TextBoxText="{Binding SampleText}"/>
</Grid>
</Window>
Below the XAML code of the custom control.
I use DataContext = Self to get the dependency property from code behind:
<UserControl x:Class="SampleApplication.CustomControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBox HorizontalAlignment="Left" Height="23" Margin="87,133,0,0" TextWrapping="Wrap" Text="{Binding TextBoxText}" VerticalAlignment="Top" Width="120"/>
</Grid>
</UserControl>
The xaml.cs file just contains the dependency property:
public partial class CustomControl : UserControl
{
public static readonly DependencyProperty TextBoxTextProperty = DependencyProperty.Register("TextBoxText", typeof (String), typeof (CustomControl), new PropertyMetadata(default(String)));
public CustomControl()
{
InitializeComponent();
}
public String TextBoxText
{
get { return (String) GetValue(TextBoxTextProperty); }
set { SetValue(TextBoxTextProperty, value); }
}
}
Thanks for any help on this. It really drives me crazy now.
EDIT:
I just came over two possible solutions:
Here the first (which works for me):
<!-- Give that child a name ... -->
<controls:ViewModel x:Name="viewModel"/>
<!-- ... and set it as ElementName -->
<controls:CustomControl TextBoxText="{Binding SampleText, ElementName=viewModel}"/>
The second one. This doesn't work in my case. I don't know why:
<controls:CustomControl TextBoxText="{Binding SampleText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:ViewModel}}}"/>
<!-- or -->
<controls:CustomControl TextBoxText="{Binding SampleText, RelativeSource={RelativeSource FindAncestor, AncestorType=controls:ViewModel}}"/>
I had a similar situation.
In my case, I fixed it with adding OnPropertyChanged in setter of property in ViewModel.
I started working with JavaFX just today and already need some advise. I load the applicaton.fxml (created with Oracle SceneBuiler) using the FXMLLoader in the start(Stage ...) method of the MainApplication (which has an ApplicationController specified in my application.fxml file).
<AnchorPane id="AnchorPane" disable="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" styleClass="theme" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="app.AppController">
...more code here...
<ComboBox id="cmb_locations" fx:id="cmb_locations">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="Item 1" />
<String fx:value="Item 2" />
<String fx:value="Item 3" />
</FXCollections>
</items>
</ComboBox>
Now, I have a ComboBox in the applicaton.fxml, which has three items (the default items). What I need is to populate that ComboBox during the startup with my own values. Does anyone know how to achieve that and where to put the relevant code snippets (app.AppController or something similar)? Thanks in advance.
You have some controller for you fxml file. There you have access to your ComboBox. You could put this code to setup list of elements (probably in initialize() method):
If you don't really want to edit your fxml file you can just clear the list first with cmb_locations.getItems().clear(); before you setup new list.
public class ApplicationController implements Initializable {
#FXML
ComboBox cmb_locations;
...
#Override
public void initialize(URL url, ResourceBundle rb) {
...
List<String> list = new ArrayList<String>();
list.add("Item A");
list.add("Item B");
list.add("Item C");
ObservableList obList = FXCollections.observableList(list);
cmb_locations.getItems().clear();
cmb_locations.setItems(obList);
...
}
}
Start by removing the default values on the FXML "Item 1" "Item 2" ...
just to have
<FXCollections fx:factory="observableArrayList">
</FXCollections>
and on your controller if you want retrieve your combobox you have to inject it by doing
#FXML
ComboBox cmb_locations
public void initialize(URL url, ResourceBundle resource) {
//here populate your combobox
}
In your controller, you implement the Initializable interface.
Then in initialize method, you just add your code to load your combo box.
Can anyone give out the best practices for playing around with controls at runtime such as creating a new view,adding views inside a view,adding controls to containers using MVVM pattern without breaking mvvm pattern??
I am using MVVMlight toolkit..
please help me out in this regard..
Thanks in advance......
This post discusses the strategies for creating views (dialogs) from your view models.
Edit:
From your comment I take it that you got an user interface that has an add and delete button. The add button should add an item (type ?) to a ItemsControl ... hope that's correct.
So, how would I do this, well I would create a view model that has an ObservableCollecion<ItemViewModel>. The ItemViewModle is the view mode that represents the item that should be added to the ItemsControl (so in your case the view model backing your "rangeView").
Then I would add two commands that handle the addition and deletion of items. Both commands just add/remove ItemViewModels from your collection.
To show the items in the view I would bind the ItemControl.ItemsSource property to the collection in your main view model (i.e. the one holding the ItemViewModel instances). The I would supply an ItemTemplate to render the items on the screen.
Ok, here is an example of what I think you are trying to do (at least conceptionally). Complete Source Code here. In the example I used a ListBox as it allows me easily to determine which item is selected, this depends on your szenario. Also note that you have complete freedom to customize the Template, the ItemPanelTemplate, and DataTemplate to fit your needs. You can even use this approacht to create PanoramaPages in WP7!
2nd edit: ItemsControl does not have a SelectedItem property. To get to it you have to use a control inheriting from Selector (e.g. a ListBox as I did) or you can use the Selector directly.
ItemViewModel:
public class ItemViewModel : ViewModelBase
{
#region [Name]
public const string NamePropertyName = "Name";
private string _name = null;
public string Name {
get {
return _name;
}
set {
if (_name == value) {
return;
}
var oldValue = _name;
_name = value;
RaisePropertyChanged(NamePropertyName);
}
}
#endregion
}
MainViewModel:
public class MainViewModel : ViewModelBase
{
public MainViewModel() {
if (IsInDesignMode) {
this.Items = new ObservableCollection<ItemViewModel>(Enumerable.Range(0, 10).Select((x, i) => new ItemViewModel() { Name = "Design Time Item " + i }));
} else {
// Code runs "for real"
}
}
#region [AddCommand]
private RelayCommand _addCommand;
public RelayCommand AddCommand {
get {
return _addCommand ?? (_addCommand = new RelayCommand(
() => {
this.Items.Add(new ItemViewModel() { Name = "New item - " + DateTime.Now });
}
));
}
}
#endregion
#region [DeleteCommand]
private RelayCommand _deleteCommand;
public RelayCommand DeleteCommand {
get {
return _deleteCommand ?? (_deleteCommand = new RelayCommand(
() => {
this.Items.Remove(this.SelectedItem);
},
() => { return this.SelectedItem != null; }
));
}
}
#endregion
#region [Items]
public const string ItemsPropertyName = "Items";
private ObservableCollection<ItemViewModel> _items = new ObservableCollection<ItemViewModel>();
public ObservableCollection<ItemViewModel> Items {
get {
return _items;
}
set {
if (_items == value) {
return;
}
var oldValue = _items;
_items = value;
RaisePropertyChanged(ItemsPropertyName);
}
}
#endregion
#region [SelectedItem]
public const string SelectedItemPropertyName = "SelectedItem";
private ItemViewModel _selectedItem = null;
public ItemViewModel SelectedItem {
get {
return _selectedItem;
}
set {
if (_selectedItem == value) {
return;
}
var oldValue = _selectedItem;
_selectedItem = value;
RaisePropertyChanged(SelectedItemPropertyName);
// important in SL to notify command that can execute has changed !
this.DeleteCommand.RaiseCanExecuteChanged();
}
}
#endregion
}
MainPage.xaml
<UserControl
x:Class="MvvmLight1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="300"
Width="300"
DataContext="{Binding Main, Source={StaticResource Locator}}"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<!-- we are dealing with ItemViewModels now -->
<Border BorderThickness="0,0,0,1" BorderBrush="Gray" Padding="10,5">
<TextBlock Text="{Binding Name}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Margin="5,10" Content="Add" Command="{Binding AddCommand}" Width="70"/>
<Button Margin="5,10" Content="Delete" Command="{Binding DeleteCommand}" Width="70"/>
</StackPanel>
</Grid>
</UserControl>