It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
The first thanks Charleh for caliburn.micro navigation solution.
How to Active SubMenu navigate to Page2 and Deactive if navigate other page?
ShellView.xaml
<Window x:Class="Navigation.ShellView"
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"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600" WindowStartupLocation="CenterScreen" Width="800" Height="600">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--Header-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<ContentControl Grid.Column="1" Grid.Row="0" x:Name="MainMenuRegion" HorizontalContentAlignment="Stretch" HorizontalAlignment="Right" Margin="0,9,17,0" />
<ContentControl Grid.Column="1" Grid.Row="1" x:Name="SubMenuRegion" HorizontalContentAlignment="Stretch" HorizontalAlignment="Right" Margin="0,0,17,0" />
</Grid>
<!--Content-->
<ContentControl Grid.Row="2" x:Name="ActiveItem"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
</ContentControl>
</Grid>
</Window>
ShellViewModel.cs
namespace Navigation
{
public class ShellViewModel : Conductor<object>.Collection.OneActive, IShellViewModel, IHandle<NavigationEventMessage>
{
public ShellViewModel(IEventAggregator eventAggregator, INavigationService navigationService, IPage1ViewModel page1ViewModel, IPage2ViewModel page2ViewModel,IMainMenuViewModel mainMenuViewModel, ISubMenuViewModel subMenuViewModel)
{
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
navigationService.Navigate(typeof(IPage1ViewModel), null);
_page1ViewModel = page1ViewModel;
_page2ViewModel = page2ViewModel;
Items.Add(_page1ViewModel);
Items.Add(_page2ViewModel);
ActiveItem = _page1ViewModel;
}
private readonly IEventAggregator _eventAggregator;
private readonly IPage1ViewModel _page1ViewModel;
private readonly IPage2ViewModel _paage2ViewModel;
public IMainMenuViewModel MainMenuRegion { get; set; }
public ISubMenuViewModel SubMenuRegion { get; set; }
public void Handle(NavigationEventMessage message)
{
ActivateItem(message.ViewModel);
}
}
}
public interface IShellViewModel
{
}
public interface INavigationService
{
void Navigate(Type viewModelType, object modelParams);
}
public class NavigationEventMessage
{
public IScreen ViewModel { get; private set; }
public NavigationEventMessage(IScreen viewModel)
{
ViewModel = viewModel;
}
}
public class NavigationService : INavigationService
{
// Depends on the aggregator - this is how the shell or any interested VMs will receive
// notifications that the user wants to navigate to someplace else
private IEventAggregator _aggregator;
public NavigationService(IEventAggregator aggregator)
{
_aggregator = aggregator;
}
// And the navigate method goes:
public void Navigate(Type viewModelType, object modelParams)
{
// Resolve the viewmodel type from the container
var viewModel = IoC.GetInstance(viewModelType, null);
// Inject any props by passing through IoC buildup
IoC.BuildUp(viewModel);
// Check if the viewmodel implements IViewModelParams and call accordingly
var interfaces = viewModel.GetType().GetInterfaces()
.Where(x => typeof(IViewModelParams).IsAssignableFrom(x) && x.IsGenericType);
// Loop through interfaces and find one that matches the generic signature based on modelParams...
foreach (var #interface in interfaces)
{
var type = #interface.GetGenericArguments()[0];
var method = #interface.GetMethod("ProcessParameters");
if (type.IsAssignableFrom(modelParams.GetType()))
{
// If we found one, invoke the method to run ProcessParameters(modelParams)
method.Invoke(viewModel, new object[] { modelParams });
}
}
// Publish an aggregator event to let the shell/other VMs know to change their active view
_aggregator.Publish(new NavigationEventMessage(viewModel as IScreen));
}
}
// This is just to help with some reflection stuff
public interface IViewModelParams { }
public interface IViewModelParams<T> : IViewModelParams
{
// It contains a single method which will pass arguments to the viewmodel after the nav service has instantiated it from the container
void ProcessParameters(T modelParams);
}
public class ViewInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
// The 'true' here on the InSameNamespaceAs causes windsor to look in all sub namespaces too
container.Register(Component.For<IShellViewModel>().ImplementedBy<ShellViewModel>().LifestyleSingleton());
container.Register(Component.For<IPage1ViewModel>().ImplementedBy<Page1ViewModel>().LifestyleSingleton());
container.Register(Component.For<IPage2ViewModel>().ImplementedBy<Page2ViewModel>().LifestyleSingleton());
container.Register(Component.For<IMainMenuViewModel>().ImplementedBy<MainMenuViewModel>().LifestyleSingleton());
container.Register(Component.For<ISubMenuViewModel>().ImplementedBy<SubMenuViewModel>().LifestyleSingleton());
}
}
public class NavigationInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<INavigationService>().ImplementedBy<NavigationService>());
}
}
public class CaliburnMicroInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
// Register the window manager
container.Register(Component.For<IWindowManager>().ImplementedBy<WindowManager>());
// Register the event aggregator
container.Register(Component.For<IEventAggregator>().ImplementedBy<EventAggregator>());
}
}
MainMenuView.xaml
<UserControl x:Class="Navigation.Views.MainMenuView"
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"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top">
<RadioButton IsChecked="True" Content="Page1" cal:Message.Attach="[Event Checked]=[Action Page1Checked]"/>
<RadioButton Content="Page2" cal:Message.Attach="[Event Checked]=[Action Page2Checked]"/>
</StackPanel>
</Grid>
</UserControl>
MainMenuViewModel.cs
namespace Navigation.ViewModels
{
public class MainMenuViewModel : Conductor<object>.Collection.OneActive, IMainMenuViewModel
{
private readonly IEventAggregator _eventAggregator;
private bool _isAssemblyManagementModule;
public MainMenuViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
public void Page1Checked()
{
NavigationService navigationService = new NavigationService(_eventAggregator);
navigationService.Navigate(typeof(IPage1ViewModel), null);
}
public void Page2Checked()
{
NavigationService navigationService = new NavigationService(_eventAggregator);
navigationService.Navigate(typeof(IPage2ViewModel), null);
}
}
public interface IMainMenuViewModel
{
}
}
SubMenuView.xaml
<UserControl x:Class="Navigation.Views.SubMenuView"
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"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top">
<RadioButton IsChecked="True" Content="SubPage1" cal:Message.Attach="[Event Checked]=[Action SubPage1Checked]"/>
<RadioButton Content="SubPage2" cal:Message.Attach="[Event Checked]=[Action SubPage2Checked]"/>
</StackPanel>
</Grid>
</UserControl>
SubMenuViewModel.cs
namespace Navigation.ViewModels
{
public class SubMenuViewModel : Conductor<object>.Collection.OneActive, ISubMenuViewModel
{
private readonly IEventAggregator _eventAggregator;
private bool _isAssemblyManagementModule;
public SubMenuViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
public void SubPage1Checked()
{
NavigationService navigationService = new NavigationService(_eventAggregator);
navigationService.Navigate(typeof(ISubPage1ViewModel), null);
}
public void SubPage2Checked()
{
NavigationService navigationService = new NavigationService(_eventAggregator);
navigationService.Navigate(typeof(ISubPage2ViewModel), null);
}
}
public interface ISubMenuViewModel
{
}
}
Not really an answer to the actual question but more a warning: You have some other issues with the IoC container and how you are using it
You are creating a new navigation service instance for each command in your viewmodels...that's not following the IoC or service methodology.
The navigation service should be resolved by the container so your viewmodel constructor should contain an INavigationService parameter
e.g. your constructor for MainMenuViewModel should look like this:
private INavigationService _navigationService;
public MainMenuViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
}
... and usage:
public void Page1Checked()
{
_navigationService.Navigate(typeof(IPage1ViewModel), null);
}
This is because the container will automatically inject the INavigationService implementation into your VM. You don't need a reference to IEventAggregator implementation (unless your VM is dependent on it, which it doesn't appear to be) and you should not be manually instantiating the NavigationService instance since this is the job of the container
Is this your first time using IoC or MVVM? Can you post some more information (maybe with screenshots) on what you are experiencing and what you expect?
Edit:
Ok this is all I can give you until I know what you are doing with the project you sent me (prism or move to CM?)
Caliburn.Micro uses IConductor<T> as the base for all windows which may conduct/manage an active screen/item. The Conductor<T> is the standard implementation of this interface and is usually used to manage IScreen based concretions.
IConductor interface
http://caliburnmicro.codeplex.com/SourceControl/changeset/view/ae25b519bf1e46a506c85395f04aaffb654c0a08#src/Caliburn.Micro.Silverlight/IConductor.cs
and implementation
http://caliburnmicro.codeplex.com/SourceControl/changeset/view/ae25b519bf1e46a506c85395f04aaffb654c0a08#src/Caliburn.Micro.Silverlight/Conductor.cs
The default conductor manages 1 screen. There are a couple of nested classes which implement multiple screens - Conductor<T>.Collection.OneActive and Conductor<T>.Collection.AllActive allowing either one or all of its items to be active at a time - (e.g. an example of OneActive is the Internet Explorer window with tabs and an example of AllActive is Visual Studio 2010 tool windows)
In order to work with this conductor implementation you should ideally use IScreen (or the concrete Screen class)
IScreen interface
http://caliburnmicro.codeplex.com/SourceControl/changeset/view/ae25b519bf1e46a506c85395f04aaffb654c0a08#src/Caliburn.Micro.Silverlight/IScreen.cs
and implementation
http://caliburnmicro.codeplex.com/SourceControl/changeset/view/ae25b519bf1e46a506c85395f04aaffb654c0a08#src/Caliburn.Micro.Silverlight/Screen.cs
Basically, with these implementations, if the main window is deactivated, it is easy to bubble the 'deactivate' message down to all the children and their children and so on. It also means that a screen gets a notification that it has been activated (OnActivate etc)
e.g.
class ParentWindow : Conductor<IScreen>
{
void DoSomething()
{
ActivateItem(ItemToActivate); // Previous item is automatically deactivated etc
}
override OnActivate()
{
// Activate children or whatever
}
}
class SomeChildWindow : Screen
{
}
It's important to note that Conductor<T> subclasses Screen so it's possible to have child conductors and grandchild conductors that will all obey lifecycle.
I'm not sure if there are Prism equivalents
There is some good Caliburn Micro documentation regarding screens and lifecycle:
http://caliburnmicro.codeplex.com/wikipage?title=Screens%2c%20Conductors%20and%20Composition&referringTitle=Documentation
If you are still using Prism and not Caliburn.Micro and there is no Prism equivalent, then at least the Caliburn.Micro implementations will give you some guidance
Related
I have a Xamarin CarouselView initialized with an empty ObservableCollection. The Carousel view correctly shows the EmptyView on page load.
However, when I add an item to the Observable Collection iOS throws the following exception:
attempt to insert item 0 into section 0, but there are only 0 items in section 0 after the update
The way I'm populating the item source is after the page loads I've hooked a button to an event handler to add items to the Observable Collection.
If I initialize the Observable Collection with initial data then the CarouselView works fine, it's just when I add items later that it breaks.
Page:
...
<ContentPage.BindingContext>
<viewModel:MatchesPageViewModel></viewModel:MatchesPageViewModel>
</ContentPage.BindingContext>
<CarouselView Margin="-10,15,-10,0"
ItemsSource="{Binding PendingMatches}"
HorizontalOptions="FillAndExpand"
HorizontalScrollBarVisibility="Never"
IsSwipeEnabled="True"
VerticalOptions="StartAndExpand">
<CarouselView.EmptyView>
<Frame>
<Label Text="It's empty"></Label>
</Frame>
</CarouselView.EmptyView>
<CarouselView.ItemTemplate>
<DataTemplate>
<Label Text="Some Content"></Label>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
View Model:
public class MatchesPageViewModel : BaseViewModel
{
public ObservableCollection<MatchResponse> PendingMatches { get; set; } =
new ObservableCollection<MatchResponse>();
//pretend it's invoked from a button in the page
public void SomeEventHandler()
{
//throws exception: attempt to insert item 0 into section 0...
PendingMatches.Add(new MatchResponse());
}
}
I guess that you may have some problem about adding item in observablecollection for Button click method. I do one demo using Button command binding that you can take a look.
<CarouselView
HorizontalOptions="FillAndExpand"
HorizontalScrollBarVisibility="Never"
IsSwipeEnabled="True"
ItemsSource="{Binding PendingMatches}"
VerticalOptions="StartAndExpand">
<CarouselView.EmptyView>
<Frame>
<Label Text="It's empty" />
</Frame>
</CarouselView.EmptyView>
<CarouselView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding str}" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<Button
x:Name="btn1"
Command="{Binding command1}"
Text="add data" />
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
this.BindingContext = new MatchesPageViewModel();
}
}
public class MatchesPageViewModel
{
public ObservableCollection<MatchResponse> PendingMatches { get; set; } = new ObservableCollection<MatchResponse>();
public ICommand command1 { get; set; }
public MatchesPageViewModel()
{
command1 = new Command(SomeEventHandler);
}
public void SomeEventHandler()
{
//throws exception: attempt to insert item 0 into section 0...
PendingMatches.Add(new MatchResponse() { str = "test" });
}
}
public class MatchResponse
{
public string str { get; set; }
}
I try to bind an pivot to the DataContext in an Windows Universal app.
Everything works fine except it seems that I am unable to mix binding and "static" PivotItems.
I need to create 0 to n PivotItems based on a list and on static PivotItem containing settings.
This is what I tried. If I remove the HeaderTemplate and ItemTemplate Element the PivotItem-Element is shown. If I let the Template Elements on there place the bound data is shown but not the extra PivotItem.
Is it even possible to mix?
<Pivot Name="PivotMain" Title="Title" ItemsSource="{Binding Path=Parts}">
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="viewmodel:DetailModel">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate x:DataType="viewmodel:DetailModel">
<TextBlock Text="TestTest"/>
</DataTemplate>
</Pivot.ItemTemplate>
<PivotItem Name="Settings" Header="Settings">
<ScrollViewer VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
<ListView ItemsSource="{Binding Path=Settings}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewmodel:SettingModel">
<RelativePanel>
<ToggleSwitch Name="OnOff"
OffContent="{Binding OffContent}" OnContent="{Binding OnContent}" IsOn="{Binding IsMonitored, Mode=TwoWay}"
RelativePanel.AlignLeftWithPanel="True" />
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</PivotItem>
</Pivot>
Is it even possible to mix?
As far as I known, it is impossible. But you can use other ways to meet your requirements. What you actually want to do is the first one PivotItem has different content with others. You should be able use a DataTemplateSelector that you can select a different DataTemplate for the first item (which header is settings) with others. For example, code as follows:
XAML
<Page.Resources>
<DataTemplate x:Key="itemstemplate" x:DataType="local:DetailModel">
<TextBlock Text="TestTest"/>
</DataTemplate>
<DataTemplate x:Key="settingtemplate" x:DataType="local:DetailModel">
<ScrollViewer VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
<ListView ItemsSource="{Binding Path=Settingss}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:SettingModel">
<RelativePanel>
<ToggleSwitch Name="OnOff" OffContent="{Binding OffContent}" OnContent="{Binding OnContent}" IsOn="{Binding IsMonitored, Mode=TwoWay}" RelativePanel.AlignLeftWithPanel="True" />
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</DataTemplate>
<local:PivotTemplateSelector
itemstemplate="{StaticResource itemstemplate}"
settingtemplate="{StaticResource settingtemplate}"
x:Key="PivotTemplateSelector" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Pivot Name="PivotMain" Title="Title" ItemsSource="{Binding Path=Parts}" ItemTemplateSelector="{StaticResource PivotTemplateSelector}">
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="local:DetailModel">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
</Pivot>
</Grid>
Code behind
public sealed partial class MainPage : Page
{
ObservableCollection<DetailModel> Parts;
ObservableCollection<SettingModel> Settingss;
public MainPage()
{
this.InitializeComponent();
Settingss = new ObservableCollection<SettingModel>()
{
new SettingModel()
{
IsMonitored=true,
OffContent="work at",
OnContent="content"
}
};
Parts = new ObservableCollection<DetailModel>()
{
new DetailModel()
{
Name="Settings",
Settingss=Settingss
},
new DetailModel()
{
Name="test1"
},
new DetailModel()
{
Name="test2"
}
};
datasources datasource = new datasources()
{
Parts = Parts
};
this.DataContext = datasource;
}
}
public class PivotTemplateSelector : DataTemplateSelector
{
public DataTemplate itemstemplate { get; set; }
public DataTemplate settingtemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
DetailModel itemget = item as DetailModel;
if (itemget.Name == "Settings")
{
return settingtemplate;
}
else
return itemstemplate;
return base.SelectTemplateCore(item);
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return SelectTemplateCore(item);
}
}
public class datasources
{
public ObservableCollection<DetailModel> Parts { get; set; }
}
public class DetailModel
{
public string Name { get; set; }
public ObservableCollection<SettingModel> Settingss { get; set; }
}
public class SettingModel
{
public string OffContent { get; set; }
public string OnContent { get; set; }
public bool IsMonitored { get; set; }
}
I have created sample User Control
RestrictedBox.xaml
<UserControl.Resources>
<Converters:EnumToVisibilityConverter x:Key="enumToVisConverter" />
<Converters:EnumToVisibilityConverterReverse x:Key="enumToVisConverterReverse" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" Width="Auto">
<StackPanel Margin="0">
<TextBox Text="{Binding Value}" Visibility="{Binding Type,Converter={StaticResource enumToVisConverter}}" BorderBrush="Green" />
<PasswordBox Password="{Binding Value}" Visibility="{Binding Type,Converter={StaticResource enumToVisConverterReverse}}" BorderBrush="Red" />
</StackPanel>
</Grid>
RestrictedBox.xaml.cs
public partial class RestrictedBox : UserControl
{
public RestrictedBox()
{
InitializeComponent();
//If i bind static value and uncomment following line then it is working.
//If i bind static value and comment following line then it is not working
this.DataContext = this;
//Dynamic binding does not work using this code.
}
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(RestrictedBox), new PropertyMetadata("", ValueChanged));
private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public Mode Type
{
get { return (Mode)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register("Type", typeof(Mode), typeof(RestrictedBox), new PropertyMetadata(TypeChanged));
private static void TypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
LoginViewModel.cs
public class LoginViewModel : INotifyPropertyChanged, IRegionMemberLifetime
{
private string _UserName = "Imdadhusen Sunasara";
public string UserName
{
get { return _UserName; }
set
{
_UserName = value;
OnPropertyChanged("UserName");
}
}
}
LoginView.xaml (This following line does not work with binding)
<control:RestrictedBox Value="{Binding UserName}" Type="Text" />
This is working (with static binding)
<control:RestrictedBox Value="Imdadhusen" Type="Text" />
Thanks,
Imdadhusen
Actually It should work. Can you please verify that the DataContext of parent container of below control doesn't refering to any other property of viewmodel.
<control:RestrictedBox Value="Imdadhusen" Type="Text" />
eg. Something like below.
<StackPanel DataContext={Binding CurrentUser}>
<control:RestrictedBox Value="{Binding UserName}"
Type="Text" />
</StackPanel>
May be this help you....
I have got solution from following
http://forums.silverlight.net/t/250206.aspx/1?Dynamic+binding+with+User+Control+does+not+work+as+Static+is+working+in+Silverlight+and+MVVM
Thanks everybody who trying to help me.
Imdadhusen
I get some trouble with the binding using MVVM toolkit and would likr to knwo some advise if I do things correctly. First of all I have the View Model Locator which is defined as follow :
public class ViewModelLocator
{
private static MainViewModel _main;
private static ProductViewModel _product;
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
////if (ViewModelBase.IsInDesignModeStatic)
////{
//// // Create design time view models
////}
////else
////{
//// // Create run time view models
////}
CreateMain();
CreateProduct();
}
/// <summary>
/// Gets the Main property.
/// </summary>
public static MainViewModel MainStatic
{
get
{
if (_main == null)
{
CreateMain();
}
return _main;
}
}
/// <summary>
/// Gets the Main property.
/// </summary>
public static ProductViewModel ProductStatic
{
get
{
if (_product == null)
{
CreateProduct();
}
return _product;
}
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return MainStatic;
}
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public ProductViewModel Product
{
get
{
return ProductStatic;
}
}
/// <summary>
/// Provides a deterministic way to delete the Main property.
/// </summary>
public static void ClearMain()
{
_main.Cleanup();
_main = null;
}
/// <summary>
/// Provides a deterministic way to create the Main property.
/// </summary>
public static void CreateMain()
{
if (_main == null)
{
_main = new MainViewModel();
}
}
/// <summary>
/// Provides a deterministic way to create the Main property.
/// </summary>
public static void CreateProduct()
{
if (_product == null)
{
_product = new ProductViewModel();
}
}
/// <summary>
/// Cleans up all the resources.
/// </summary>
public static void Cleanup()
{
ClearMain();
}
}
Then I have my main window for which I set the datacontext as :
DataContext="{Binding Source={x:Static vm:ViewModelLocator.MainStatic}}"
Inside my main window I have a list box which will have as ItemSource a collection of ProductViewModel define as follow :
public class ProductViewModel : ViewModelBase
{
SvcProduct.ProductServiceClient _clientSvc =new SvcProduct.ProductServiceClient() ;
ObservableCollection<Product> _products = new ObservableCollection<Product>();
public ObservableCollection<Product> Products
{
get { return _products; }
set { _products = value; }
}
/// <summary>
/// Initializes a new instance of the ProductViewModel class.
/// </summary>
public ProductViewModel()
{
////if (IsInDesignMode)
////{
//// // Code runs in Blend --> create design time data.
////}
////else
////{
//// // Code runs "for real": Connect to service, etc...
////}
_products=_clientSvc.GetProducts();
}
////public override void Cleanup()
////{
//// // Clean own resources if needed
//// base.Cleanup();
////}
}
ProductViewModel return in Products the collection for the listbox. Each item in the list box is connected to a ProductView which is a userControl define as follow :
<UserControl
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"
xmlns:vm="clr-namespace:Solatys.Presentation.ViewModel"
mc:Ignorable="d"
x:Class="Solatys.Presentation.ProductView"
x:Name="UserControl"
d:DesignWidth="640" d:DesignHeight="480" Width="433" Height="319"
IsManipulationEnabled="True"
DataContext="{Binding Source={x:Static vm:ViewModelLocator.ProductStatic}}">
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="144.3"/>
<ColumnDefinition Width="0.64*"/>
<ColumnDefinition Width="144.3"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="35" MaxHeight="35"/>
<RowDefinition Height="130" MaxHeight="130"/>
<RowDefinition Height="130" MaxHeight="130"/>
<RowDefinition Height="24" MaxHeight="24"/>
</Grid.RowDefinitions>
<Image Source="Resources/beauté.jpg" Grid.Row="1" Stretch="UniformToFill" Grid.RowSpan="2" Grid.ColumnSpan="3"/>
<TextBlock TextWrapping="Wrap" Grid.Column="1" Text="{Binding ProductName}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="16" Foreground="White" FontFamily="Perpetua Titling MT" TextAlignment="Justify"/>
<Border BorderBrush="Black" BorderThickness="0" Grid.Row="3" Grid.ColumnSpan="3">
<Border.Background>
<RadialGradientBrush>
<GradientStop Color="#00000000" Offset="1"/>
<GradientStop Color="#FF005E01"/>
</RadialGradientBrush>
</Border.Background>
<TextBlock TextWrapping="Wrap" Text="Coup de Coeur" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Perpetua" TextAlignment="Justify" FontSize="13.333"/>
</Border>
<Border Grid.Row="1" Grid.Column="0" Background="#7F000000" d:LayoutOverrides="Width">
<TextBlock TextWrapping="Wrap" Foreground="White" FontFamily="Perpetua" Margin="5"><Run Text="Type de produit"/><Run Language="fr-fr" Text=" : "/><LineBreak/><Run Language="fr-fr"/><LineBreak/><Run Language="fr-fr" Text="Produit de beauté Bio"/></TextBlock>
</Border>
<Border HorizontalAlignment="Right" Grid.Row="1" Grid.Column="2" Width="144.3" Height="130" Background="#7F000000">
<TextBlock Margin="5" TextWrapping="Wrap" Foreground="White" FontFamily="Perpetua"><Run Text="Court descriptif"/><LineBreak/><Run/><LineBreak/><Run Language="fr-fr" Text="Ce nouveau produit reste notre coup de coeur pour ses propriétés naturelles ..."/></TextBlock>
</Border>
<Border HorizontalAlignment="Center" Grid.Row="2" Grid.Column="1" Width="144.3" Height="130" Background="#7F000000">
<TextBlock TextWrapping="Wrap" Margin="5" Foreground="White" FontFamily="Perpetua"><Run Text="Caractéristiques du produit"/><Run Language="fr-fr" Text=" : "/><LineBreak/><Run Language="fr-fr"/><LineBreak/><Run Language="fr-fr" Text="- rajeunissant"/><LineBreak/><Run Language="fr-fr" Text="- vivifiant"/><LineBreak/><Run Language="fr-fr" Text="- prix attractif"/><LineBreak/><Run Language="fr-fr" Text="- produit contrôlé"/></TextBlock>
</Border>
</Grid>
As you can see the above the DataContext is set to ViewModelLocator.ProductStatic but it says an error like "Cannot create instance of ViewModelLocator"
Due to that it seems that the binding is not operating as my list box is empty on my main Window
1- Any idea what I do wrong for that error?
2- In my scenario, how should I bind the ItemSource as the collection is a collection of ProductViewModel ?
regards
serge
"Cannot create instance of ViewModelLocator" is typically a sign that something went wrong while the VMs were created. Try to put a breakpoint on _products=_clientSvc.GetProducts() then debug the code. I am pretty sure that something is going wrong in this method and an exception is thrown, which is causing the ViewModelLocator to fail too.
Cheers,
Laurent
I have built a view model and the listbox binds to the ObervableCollection, but a textbox I am using for a temp url wont bind and return data. I am not getting any errors either on compile or run
ViewModel:
public class HomepageModel:INotifyPropertyChanged
{
public TextBlock bgrImg{get;set;}
public ObservableCollection<MenuItem> menu {get; private set;}
public HomepageModel()
{
this.menu = new ObservableCollection<MenuItem>();
}
public void Load()
{
bgrImg = new TextBlock();
bgrImg.Text = "/Windows7MobileClient;component/Images/Desert.jpg";
//bgrImg = ;
menu.Add(new MenuItem("Feed",""));
menu.Add(new MenuItem("Messages",""));
menu.Add(new MenuItem("Media",""));
menu.Add(new MenuItem("Favourites",""));
menu.Add(new MenuItem("Freinds",""));
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Homepage.xaml
<controls:PanoramaItem Header="menu">
<Grid>
<TextBlock Text="{Binding bgrImg}"/>
<ListBox x:Name="FirstListBox" Margin="0,0,-12,0" ItemsSource="{Binding menu}" MouseLeftButtonUp="FirstListBox_MouseLeftButtonUp" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding label}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</controls:PanoramaItem>
I eventual want to use the string for the panorama background image. Sorry if this seems realy obviously simply.
Chris
You need to call NotifyPropertyChanged() in your setters for the items you wish to bind to.
One more thing I noticed: You are trying to Bind TextBlock to a string Property:
public TextBlock bgrImg{get;set;}
<TextBlock Text="{Binding bgrImg}"/>
Change the type of the property to string:
public string bgrImg{get;set;}
<TextBlock Text="{Binding bgrImg}"/>