i'm new in the mvvm pattern and when i've trying to binding an property to text box i always get this error in the output debugger :
"System.Windows.Data Error: BindingExpression path error: 'MainView' property not found on 'DesktopContainerViewModel' 'System.String' (HashCode=-115914272). BindingExpression: Path='MainView' DataItem='DesktopContainerViewModel' (HashCode=-115914272); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String').."
Xaml Code :
<UserControl x:Class="PlanNetApp.UI.DesktopContainerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:b="clr-namespace:PlanNetApp.Controls.Behaviors;assembly=PlanNetApp.Controls"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ctrl="clr-namespace:PlanNetApp.Controls.Custom;assembly=PlanNetApp.Controls"
xmlns:conv="clr-namespace:PlanNetApp.Controls.Convertors;assembly=PlanNetApp.Controls"
xmlns:v="clr-namespace:PlanNetApp.UI.Views"
xmlns:vb="clr-namespace:PlanNetApp.UI.Views.Boq"
xmlns:ctrlui="clr-namespace:PlanNetApp.UI.Controls"
xmlns:vp="clr-namespace:PlanNetApp.UI.Views.Project"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="clr-namespace:PlanNetApp.UI"
mc:Ignorable="d"
DataContext="DesktopContainerViewModel"
d:DataContext="{d:DesignData Source=C:\\RASEMP_Code\\DROP2\\Desktop\\DesktopV1\\PlanNetTestApp\\PlanNetApp.UI\\DesignTime\\DesktopContainerSample.xaml}"
d:DesignHeight="600" d:DesignWidth="800"
FlowDirection="LeftToRight"
>
<UserControl.Resources>
<conv:BooleanToBusyIndicatorStatus x:Key="BooleanToBusyIndicatorStatusConver"/>
<conv:IsEqualParameterToVisibility x:Key="IsEqualParameterToVisibilityConverter"/>
<conv:IsEqualParameterToBool x:Key="IsEqualParameterToBool" />
<conv:EnumValues x:Key="EnumValuesConverter" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" >
<Grid.Background>
<LinearGradientBrush StartPoint="0.5, 0" EndPoint="0.5,1">
<GradientStop Color="#FF4D4D4D" Offset="0"/>
<GradientStop Color="#FF2B2B2B" Offset="0.599"/>
<GradientStop Color="#FF0B0B0B" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<Rectangle
b:Texture.ImageSource="../Images/Textures/Wood.png"
Opacity="0.485"
/>
<Grid x:Name="main" >
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid Height="50" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="20"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="20"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="20"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ctrl:HoverText Text="Exit"
ForGroundText="#9d9c9c"
Grid.Column="1"></ctrl:HoverText>
<ctrl:HoverText Text="Somebody"
ForGroundText="#9d9c9c"
Grid.Column="3"></ctrl:HoverText>
<ctrl:HoverText Text="Support"
ForGroundText="#9d9c9c"
Grid.Column="5"></ctrl:HoverText>
*<TextBlock Text="{Binding Path=MainView}" Grid.Column="7"/>*
</Grid>
</Grid>
</Grid>
</UserControl>
the is no code behind (mvvm pattern :) )
the vieModel looks like:
viewmodel.cs:
public enum DesktopMainView
{
None,
Desktop,
Apps
}
public enum AppMainView
{
None,
Projects,
Prices
}
public class DesktopContainerViewModel : AnnotatedViewModel
{
#region MView property
public const String MViewPropertyName = "MView";
private DesktopMainView p_MView;
public DesktopMainView MView
{
get
{
return p_MView;
}
set
{
if (p_MView != value)
{
p_MView = value;
_notifyPropertyChanged(MViewPropertyName);
}
}
}
#endregion
#region MainView property
public const String MainViewPropertyName = "MainView";
private String p_MainView;
public String MainView
{
get
{
return p_MainView;
}
set
{
if (p_MainView != value)
{
p_MainView = value;
_notifyPropertyChanged(MainViewPropertyName);
}
}
}
#endregion
#region AppMainView property
public const String AppMainViewPropertyName = "AppMainView";
private AppMainView p_AppMainView;
public AppMainView AppMainView
{
get
{
return p_AppMainView;
}
set
{
if (p_AppMainView != value)
{
p_AppMainView = value;
_notifyPropertyChanged(AppMainViewPropertyName);
}
}
}
#endregion
#region NavigateToProjectDetailsCommand property
public const String NavigateToProjectDetailsCommandPropertyName = "NavigateToProjectDetailsCommand";
private RelayCommand p_NavigateToProjectDetailsCommand;
public RelayCommand NavigateToProjectDetailsCommand
{
get
{
return p_NavigateToProjectDetailsCommand;
}
set
{
if (p_NavigateToProjectDetailsCommand != value)
{
p_NavigateToProjectDetailsCommand = value;
_notifyPropertyChanged(NavigateToProjectDetailsCommandPropertyName);
}
}
}
#endregion
private void ExecuteNavigateToProjectDetailsCommand(object param)
{
FrameworkElement anchor = param as FrameworkElement;
if (MView == DesktopMainView.Desktop)
{
MView = DesktopMainView.Apps;
AppMainView = AppMainView.Projects;
}
}
public DesktopContainerViewModel()
{
NavigateToProjectDetailsCommand = new RelayCommand(ExecuteNavigateToProjectDetailsCommand);
MView = DesktopMainView.Desktop;
AppMainView = AppMainView.None;
MainView = MView.ToString();
}
public override void Cleanup()
{
throw new NotImplementedException();
}
protected override void OnAnnotationDirty()
{
throw new NotImplementedException();
}
public override string AnnotationTitle()
{
throw new NotImplementedException();
}
}
in my viemodel there is a property called MainView!!
in addition i'm using a designTime Binding and with this binding i can see that the binding
works..
What can be the Problem??
The problem is here :
DataContext="DesktopContainerViewModel"
Try to set the DataContext like this in the code-behind :
this.DataContext = new DesktopContainerViewModel();
Related
I am downloading data from an api, and displaying that data in the view. As I wait I want to display a ProgressRing, but when I bind it dosen't work
Bind the ring to the cityData property, with a two way mode and update it when the property changes
Created a new property in the VM, that is true by default and it will turn false when I get the data back
XAML
<TextBox x:Name="currentLocation"
PlaceholderText="Please wait..."
IsReadOnly="True"
Margin="20"
Width="300"
Text="{Binding cityData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ListView RelativePanel.Below="currentLocation"
x:Name="ForecastList"
Margin="20"
SelectedItem="{Binding currentDay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding dailyForecasts}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<TextBlock x:Name="dateTB" Text="{Binding Date.DayOfWeek}" />
<TextBlock x:Name="highTB" Text="{Binding Temperature.Maximum.Value, Converter={StaticResource cv}}" FontSize="10" />
<TextBlock x:Name="lowTB" FontSize="10" Text="{Binding Temperature.Minimum.Value, Converter={StaticResource cv}}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ProgressRing x:Name="pRing" RelativePanel.Above="ForecastList" RelativePanel.AlignHorizontalCenterWith="currentLocation" IsActive="{Binding ring, Mode=TwoWay}" RelativePanel.Below="currentLocation" />
VM
public class WeatherVM: INotifyPropertyChanged
{
public AccuWeather accuWeather { get; set; }
private string _cityData;
public string cityData
{
get { return _cityData; }
set
{
if (value != _cityData)
{
_cityData = value;
onPropertyChanged("cityData");
GetWeatherData();
}
}
}
private DailyForecast _currentDay;
public DailyForecast currentDay
{
get { return _currentDay; }
set
{
if (value != _currentDay) \
{
_currentDay = value;
onPropertyChanged("currentDay");
}
}
}
public bool ring { get; set; } = true;
public ObservableCollection<DailyForecast> dailyForecasts { get; set; }
public WeatherVM()
{
GetCuurentLocation();
dailyForecasts = new ObservableCollection<DailyForecast>();
}
private async void GetCuurentLocation() {
cityData = await BingLocator.GetCityData();
}
public async void GetWeatherData() {
var geoposition = await LocationManager.GetGeopositionAsync();
var currentLocationKey = await WeatherAPI.GetCityDstaAsync(geoposition.Coordinate.Point.Position.Latitude, geoposition.Coordinate.Point.Position.Longitude);
var weatherData = await WeatherAPI.GetWeatherAsync(currentLocationKey.Key);
if (weatherData != null) {
foreach (var item in weatherData.DailyForecasts) {
dailyForecasts.Add(item);
}
}
currentDay = dailyForecasts[0];
ring = false;
}
public event PropertyChangedEventHandler PropertyChanged;
private void onPropertyChanged(string property) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
The progress ring appears when the app launch, and disappear when the data is returned
From your code, it seems to be a layout issue. First you put ListView below currentLocation, then set ProgressRing above ListView and below currentLocation, so the height of ProgressRing wil be zero. I'm not clear about your layout, you can try to only set RelativePanel.Above="ForecastList" for ProgressRing to see if it will appear.
You can use Windows Community Toolkit control for showing progress ring (Busy indicator).
<Page ...
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"/>
<controls:Loading x:Name="LoadingControl" IsLoading="{Binding IsBusy}">
<!-- Loading screen content -->
</controls:Loading>
I have a ListView shown below.
How can I bind dictionary to ListView Itemsource so that my label as key and ENtry has value?
I don't know How to proceed further
I tried with this but I am getting null reference exception
<ListView x:Name="ItemsListView" VerticalOptions="FillAndExpand" SeparatorVisibility="None" HasUnevenRows="true" ItemsSource="{Binding dictionary}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Text="{Binding Key}" Grid.Row="1" Grid.Column="0" Style="{DynamicResource lblTitle}" />
<Entry x:Name="test" Text="{Binding Value}" Grid.Row="1" Grid.Column="1" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
view model
public List<string> Key
{
get { return key; }
set
{
SetProperty(ref key, value);
}
}
public List<Int32> Value
{
get { return val; }
set
{
SetProperty(ref val, value);
}
}**
for (int i = 0; i < AllProductsList.Count; i++)
{
Value.Add(0);
//Value = new ObservableCollection<Int32>(val);
}
for (int j = 0; j < AllProductsList.Count; j++)
{
for (int k = 0; k < Value.Count; k++)
{
if (j == k)
{
dictionary[Key[j]] = Value[k];
}
}
If the ItemSource is a Dictionary, then simply Binding "Key" and "Value" should work. I guess that is what you did. But You don't need to create properties "Key" and "Value". So please remove that..
//Remove these Properties
public List<string> Key
{
get { return key; }
set
{
SetProperty(ref key, value);
}
}
public List<Int32> Value
{
get { return val; }
set
{
SetProperty(ref val, value);
}
}**
What you did in your Xaml is correct.
<Grid>
<Label Text="{Binding Key}" Grid.Row="1" Grid.Column="0" Style="{DynamicResource lblTitle}" />
<Entry x:Name="test" Text="{Binding Value}" Grid.Row="1" Grid.Column="1" />
</Grid>
Label will show the Keys and Entry will show the value. Now, make the ItemSource of your List Binding your Dictionary(instead of the IList/List).
If you set the ItemSource= "{Binding YourDictionary}", then you can bind Key and Value as you did(Provided, YourDictionary is of type Dictionary<string,string>).
Because not knowing what type of your source data,if source data is a json type from web api, you can refer to this discussion to convert json object to ViewMidel.
In ListView ,ItemSource can be used as follow:
DictionaryModel.cs:
public class DictionaryModel : INotifyPropertyChanged
{
string key= string.Empty;
public string Key
{
get { return key; }
set { SetProperty(ref key, value); }
}
Int32 valueint = 0;
public Int32 Value
{
get { return valueint; }
set { SetProperty(ref valueint, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
ViewModel.cs:
public class ViewModel
{
public IList<DictionaryModel> DictionaryModels { get; private set; }
public ViewModel()
{
DictionaryModels = new List<DictionaryModel>();
// set demo data
DictionaryModels.Add(new DictionaryModel
{
Key = "Baboon",
Value= 1,
});
DictionaryModels.Add(new DictionaryModel
{
Key = "Capuchin",
Value= 2,
});
}
}
Then in ContenPage.cs , binding ViewModel:
BindingContext = new ViewModel();
Finally in Xaml :
<ListView x:Name="ItemsListView" VerticalOptions="FillAndExpand" SeparatorVisibility="None" HasUnevenRows="true" ItemsSource="{Binding DictionaryModels}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Text="{Binding Key}" Grid.Row="1" Grid.Column="0" Style="{DynamicResource lblTitle}" />
<Entry x:Name="test" Text="{Binding Value}" Grid.Row="1" Grid.Column="1" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I'm developing a UWP app and I'm facing a problem. The app uses the MVVM pattern with Template10. I have created a similar solution that recreates the problem that I'm facing. In that solution, a list of orders are displayed, the user chooses an order and then click the "Edit" button. Then a second page is displayed and pre-loaded with the previous selected order, in this second page the user can edit the order. The problem is in the second page, the data bound to comboboxes doesn't show. Maybe the problem is related to this question. In my case, the SelectedValue is set before the ItemsSource. After debugging, I have reached these lines of code in OrderEditionPage.g.cs:
private void Update_ViewModel(global::ComboApp.ViewModels.OrderEditionPageViewModel obj, int phase)
{
this.bindingsTracking.UpdateChildListeners_ViewModel(obj);
if (obj != null)
{
if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0)
{
this.Update_ViewModel_SelectedOrder(obj.SelectedOrder, phase);
}
if ((phase & (NOT_PHASED | (1 << 0))) != 0)
{
this.Update_ViewModel_BusinessAssociates(obj.BusinessAssociates, phase);
this.Update_ViewModel_TransactionTypes(obj.TransactionTypes, phase);
this.Update_ViewModel_OrderTypes(obj.OrderTypes, phase);
this.Update_ViewModel_ShowSelectedOrder(obj.ShowSelectedOrder, phase);
}
}
}
If I could achieve this line of code be executed at last, my problem would be solved: this.Update_ViewModel_SelectedOrder(obj.SelectedOrder, phase);
How could I achieve this? How does Visual Studio determine the order of this lines?
OrderEditionPage.xaml
<Page
x:Class="ComboApp.Views.OrderEditionPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:myconverters="using:ComboApp.Converters"
xmlns:t10converters="using:Template10.Converters"
mc:Ignorable="d">
<Page.Resources>
<t10converters:ChangeTypeConverter x:Key="TypeConverter" />
<myconverters:DateTimeConverter x:Key="DateTimeConverter" />
</Page.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel
Padding="15, 5"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox
Header="Order #"
Margin="5"
Width="150"
HorizontalAlignment="Left"
Text="{x:Bind ViewModel.SelectedOrder.ExternalId, Mode=TwoWay}" />
<ComboBox
Header="Business Associate"
Margin="5"
MinWidth="300"
SelectedValuePath="BusinessAssociateId"
DisplayMemberPath="Name1"
ItemsSource="{x:Bind ViewModel.BusinessAssociates}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.BusinessAssociateId, Mode=TwoWay, Converter={StaticResource TypeConverter}}" />
<DatePicker
Header="Delivery Date"
Margin="5"
MinWidth="0"
Width="200"
Date="{x:Bind ViewModel.SelectedOrder.DeliveryDate, Mode=TwoWay, Converter={StaticResource DateTimeConverter}}" />
<ComboBox
Header="Transaction"
MinWidth="200"
Margin="5"
SelectedValuePath="Value"
DisplayMemberPath="Display"
ItemsSource="{x:Bind ViewModel.TransactionTypes}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.TransactionType, Mode=TwoWay}" />
<TextBox
Header="Priority"
Margin="5"
MaxWidth="150"
HorizontalAlignment="Left"
Text="{x:Bind ViewModel.SelectedOrder.Priority}" />
<ComboBox
Header="Type"
Margin="5"
MinWidth="200"
SelectedValuePath="Value"
DisplayMemberPath="Display"
ItemsSource="{x:Bind ViewModel.OrderTypes}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.OrderType, Mode=TwoWay}" />
<TextBox
Header="Information"
Margin="5"
Height="100"
AcceptsReturn="True"
TextWrapping="Wrap"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Text="{x:Bind ViewModel.SelectedOrder.Information, Mode=TwoWay}" />
<Button
Margin="5"
Content="Show"
Width="100"
HorizontalAlignment="Right"
Command="{x:Bind ViewModel.ShowSelectedOrder}" />
</StackPanel>
</ScrollViewer>
</Page>
OrderEditionPage.xaml.cs
using ComboApp.ViewModels;
using Windows.UI.Xaml.Controls;
namespace ComboApp.Views
{
public sealed partial class OrderEditionPage : Page
{
public OrderEditionPageViewModel ViewModel => DataContext as OrderEditionPageViewModel;
public OrderEditionPage()
{
this.InitializeComponent();
}
}
}
OrderEditionPageViewModel.cs
using ComboApp.Models;
using ComboApp.Services;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Template10.Mvvm;
using Template10.Utils;
using Windows.UI.Xaml.Navigation;
namespace ComboApp.ViewModels
{
public class OrderEditionPageViewModel
: ViewModelBase
{
private IBusinessAssociateService businessAssociateService;
private Order selectedOrder;
public Order SelectedOrder
{
get { return selectedOrder; }
set { Set(ref selectedOrder, value); }
}
public ObservableCollection<object> TransactionTypes { get; set; } = new ObservableCollection<object>();
public ObservableCollection<object> OrderTypes { get; set; } = new ObservableCollection<object>();
public ObservableCollection<BusinessAssociate> BusinessAssociates { get; set; } = new ObservableCollection<BusinessAssociate>();
public OrderEditionPageViewModel(IBusinessAssociateService businessAssociateService)
{
this.businessAssociateService = businessAssociateService;
TransactionTypes.Add(new { Value = "I", Display = "Incoming" });
TransactionTypes.Add(new { Value = "O", Display = "Outgoing" });
TransactionTypes.Add(new { Value = "T", Display = "Transfer" });
OrderTypes.Add(new { Value = "M", Display = "Manual" });
OrderTypes.Add(new { Value = "A", Display = "Automatic" });
OrderTypes.Add(new { Value = "S", Display = "Semi-automatic" });
}
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
// Loading buiness associates
var response = await businessAssociateService.GetNextPageAsync();
if (response.IsSuccessful)
{
BusinessAssociates.AddRange(response.Result.Items);
}
SelectedOrder = (Order)parameter;
await base.OnNavigatedToAsync(parameter, mode, state);
}
private DelegateCommand showSelectedOrder;
public DelegateCommand ShowSelectedOrder => showSelectedOrder ?? (showSelectedOrder = new DelegateCommand(async () =>
{
await Views.MessageBox.ShowAsync(JsonConvert.SerializeObject(SelectedOrder, Formatting.Indented));
}));
}
}
It is a known issue of x:Bind when the SelectedValue of a ComboBox is sometimes set before its ItemsSource, you can read more about it here.
As a workaround you can use Bindings instead of x:Bind, but make sure that ItemsSource binding is placed before SelectedValue binding in XAML.
Alternatively you can try calling Bindings.Update() in the Page_Loaded event of your second page.
This is what I have. A mainwindow with a combobox and a tabcontrol in it, having a viewmodel attached to its datacontext.
I have a usercontrol with another viewmodel , this usercontrol contains a combobox and a datagrid.Now when I select an item from the main combobox , the viewmodel will calculate how many tabs need to be generated at runtime, and it assigns the usercontrol to each tabitem. The problem I am having is , I am unable to bind the items to be populated in the combobox of each usercontrol tab item. Below is my code
MainWindow.xaml
<Window x:Class="TsstApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TsstApp"
WindowState="Maximized"
Title="MainWindow" Height="1333" Width="1024">
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<Grid>
<ComboBox HorizontalAlignment="Left" Margin="21,22,0,0" VerticalAlignment="Top"
Width="120" ItemsSource="{Binding Items}" DisplayMemberPath="ItemName" SelectedItem="
{Binding SelectedItem}"/>
<TabControl HorizontalAlignment="Left" Height="928" Margin="10,69,0,0"
VerticalAlignment="Top" Width="996" ItemsSource="{Binding TabData}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Tech}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<vm:TabItemControl/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
Below is the mainviewmodel
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
private ItemCollection selectedItem;
public MainViewModel()
{
List<ItemCollection> ax = new List<ItemCollection>();
ax.Add(new ItemCollection() { ItemId = 0, ItemName = "Hair" });
ax.Add(new ItemCollection() { ItemId = 1, ItemName = "Fur" });
ax.Add(new ItemCollection() { ItemId = 2, ItemName = "Tail" });
this.Items = ax;
RaisePropertyChanged("Items");
this.SelectedItem = this.Items[0];
RaisePropertyChanged("SelectedItem");
}
public IList<ItemCollection> Items { get; set; }
public ItemCollection SelectedItem
{
get { return selectedItem ;}
set {
selectedItem = value;
RaisePropertyChanged("SelectedItem");
List<TabItemData> tabs = new List<TabItemData>();
switch(selectedItem.ItemName)
{
case "Hair":
for (int i = 0; i < 5;i++)
{
tabs.Add(new TabItemData() { TabId = i, TabName = "Hair"+i.ToString() });
}
break;
case "Fur":
for (int i = 0; i < 3; i++)
{
tabs.Add(new TabItemData() { TabId = i, TabName = "Fur" + i.ToString() });
}
break;
case "Tail":
for (int i = 0; i < 7; i++)
{
tabs.Add(new TabItemData() { TabId = i, TabName = "Tail" + i.ToString() });
}
break;
}
SelectedItem.TabData = tabs;
this.TabData = tabs.Select(t => new TabItemViewModel(t)).ToList();
RaisePropertyChanged("TabData");
}
}
public IList<TabItemViewModel> TabData { get; private set; }
}
Below is my usercontrol
UserControl.xaml
<UserControl x:Class="TsstApp.TabItemControl"
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:vm="clr-namespace:TsstApp"
mc:Ignorable="d"
d:DesignHeight="1333" d:DesignWidth="1024">
<UserControl.DataContext>
<vm:TabItemViewModel/>
</UserControl.DataContext>
<Grid>
<ComboBox HorizontalAlignment="Left" Margin="75,91,0,0" VerticalAlignment="Top"
Width="148" Height="27" ItemsSource="{Binding ReleaseItems}"
SelectedValuePath="ItemId" DisplayMemberPath="ItemName"/>
<TextBox HorizontalAlignment="Left" Height="31" Margin="75,183,0,0"
TextWrapping="Wrap" Text="{Binding TextContent}" VerticalAlignment="Top" Width="148"/>
</Grid>
</UserControl>
And this is the viewmodel
TabItemViewModel.cs
public class TabItemViewModel : ViewModelBase
{
public TabItemViewModel()
{
}
public TabItemViewModel(TabItemData t)
{
this.Tech = t.TabName;
List<TabReleases> az = new List<TabReleases>();
for(int i=0;i<10;i++)
{
az.Add(new TabReleases(){ReleaseId=i , ReleaseName=t.TabName + "-" + i.ToString()});
}
this.ReleaseItems = az;
}
public string Tech { get; set; }
public IList<TabReleases> ReleaseItems { get; set; }
}
Now , the tabitem header , is working fine , as you can see , I am binding the header in the mainwindows iteself, but the combobox of the usercontrol is not getting popualated.
Kindlyhelp.
I solved it finally. I was just doing one thing wrong. I was adding datacontect in the tabitemcontrol , but it is not required , as tabcontrol in mainwindows already has itemsource , which creates instances to tabitemview model
Can not do Binding
Random (MyViewModel) -> VMTestValue (MyViewModel) -> TestUserControl (MainWindow)
"MyViewModel" is ViewModel for "MainWindow"
All project: http://rusfolder.com/32608140
TestUserControl.xaml
<UserControl x:Class="TestBinding.TestUserControl"
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"
Background="Green"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Background="Gray" Margin="50" FontSize="18" Text="{Binding TestValue}" />
<Rectangle Height="200" Width="{Binding TestValue}" Fill="#FFFF1717" />
</Grid>
</UserControl>
TestUserControl.cs
...
public TestUserControl()
{
InitializeComponent();
this.DataContext = this;
}
public static readonly DependencyProperty TestValueProperty =
DependencyProperty.Register("TestValue", typeof(int), typeof(TestUserControl),
new FrameworkPropertyMetadata((int)255)
);
public int TestValue
{
set { SetValue(TestValueProperty, value); }
get
{
return (int)GetValue(TestValueProperty);
}
}
...
MyViewModel.cs
using System;
using System.Timers;
using Microsoft.Practices.Prism.ViewModel;
namespace TestBinding
{
class MyViewModel : NotificationObject
{
private int _VMTestValue;
private Timer timer;
private Random _random;
public MyViewModel()
{
_random=new Random();
timer=new Timer();
timer.Interval = 500;
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
VMTestValue = _random.Next(10, 300);
}
public int VMTestValue
{
set
{
_VMTestValue = value;
this.RaisePropertyChanged(() => this.VMTestValue);
}
get { return _VMTestValue; }
}
}
}
MainWindow.cs
...
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
}
...
MainWindow.xaml
...
<TestBinding1:TestUserControl TestValue="{Binding VMTestValue}" />
<TextBlock VerticalAlignment="Bottom" Background="Blue" FontSize="20" Text="{Binding VMTestValue}" />
...
Why can not I go to the UserControl?
in addition to baboon i do this in my projects:
remove this.DataContext = this; and add the following to your xaml
<UserControl x:Class="TestBinding.TestUserControl"
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"
Background="Green"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="uc">
<Grid>
<TextBlock Background="Gray" Margin="50" FontSize="18" Text="{Binding ElementName=uc,Path=TestValue}" />
<Rectangle Height="200" Width="{Binding ElementName=uc,Path=TestValue}" Fill="#FFFF1717" />
</Grid>
</UserControl>
You should let your UserControl inherit the DataContext:
public TestUserControl()
{
InitializeComponent();
//this.DataContext = this;
}
If you want to keep the DataContext of TestUserControl to itself, you can use RelativeSource:
<TestBinding1:TestUserControl TestValue="{Binding DataContext.VMTestValue, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />
Or use ElementName, let's say your MainWindow is named mainWin :
<TestBinding1:TestUserControl TestValue="{Binding VMTestValue, ElementName=mainWin}" />