View does not update UIElement property - wpf

I have a MainView with TabControl, which is intended to display child Views, and a ComboBox to display some values
MainView has this XAML:
<TabControl ItemsSource="{Binding ViewModelsCollection, Mode=TwoWay}"
SelectedItem="{Binding ViewModelsSelectedItem, Mode=TwoWay}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock Text="{Binding Path=Title}"/>
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
<ComboBox x:Name="cmbProfiles"
ItemsSource="{Binding Path=ProfileCollection}"
SelectedItem="{Binding Path=ProfileSelectedItem, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
DisplayMemberPath="Key">
</ComboBox>
Also, MainView has DataTemplates for child Views - to be used in the TabControl:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:OrdersViewModel}">
<v:OrdersView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ShippingViewModel}">
<v:ShippingView/>
</DataTemplate>
</Window.Resources>
ChildViews are created in MainViewModel:
ObservableCollection<ViewModelBase> collection = new ObservableCollection<ViewModelBase>();
collection.Add(new OrdersViewModel());
collection.Add(new ShippingViewModel());
this.ViewModelsCollection = collection;
Each ChildView has a button, which IsEnabled property should depend on MainView's cmbProfiles selection:
<Button IsEnabled="{Binding Path=BtnServiceSetupIsEnabled}"
Content="Next ...">
</Button>
Code in MainView to handle that:
IOrdersViewModel ordersViewModel = (IOrdersViewModel)this.ViewModelsCollection.Where(x => x.GetType().Equals(typeof(OrdersViewModel))).FirstOrDefault();
if (profileItem.Key == "Custom")
{
ordersViewModel.BtnServiceSetupIsEnabled = true;
}
else
{
ordersViewModel.BtnServiceSetupIsEnabled = false;
}
The problem occurs when I change selection in cmbProfiles: ChildViewModel.BtnServiceSetupIsEnabled gets updated in code indeed, but the ChildView doesn't reflect that change, button stays disabled.
It does work fine if I don't use DataTemplates and simply add <v:OrdersView/> to my MainView, not dynamically filling the TabControl.
How can I fix that - and to have my TabControl's TabItems created dynamically?

Related

Binding SelectedIndex property of a list to a property in different class in same/another project in Windows Phone 8?

There is something that I can't understand when I bind SelectedIndex of a list to a property. It works only if the property is part of the Codebehind of this XAML page
for example : If there we have test.xaml , test.xaml.cs and AppSettings.cs , the binding works correctly only if the SelectedIndex property is bound to a property in test.xaml.cs
After some trials , I found that if I set ItemSource and write all binding stuff in Code-behind , it works even if the property is in binder.cs !
I think is related to the order of ItemSource and SelectedItem.
The Code of the Property
private Currency sTileCurrency;
public Currency STileCurrency
{
get
{
return GetValueorDefault<Currency>("STileCurrency", null);
}
set
{
sTileCurrency = value;
if (AddOrUpdateValue("STileCurrency", value))
{
settings.Save();
}
}
}
(This won't work !)
<ListBox Name="sCurrencyLB" Margin="10,0,0,0" Width="Auto" Height="180" ItemsSource="{Binding SCurrencyList}" SelectedItem="{Binding STileCurrency, Source={StaticResource appSettings}, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<TextBlock Name="scountryNametb" Width="50" Text="{Binding code}" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Image Source="{Binding imgUrl}" Height="50" Width="50" HorizontalAlignment="Left" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But if we removed SelectedItem and ItemSource from XAML and set it in Codebehind
(This will work !)
sCurrencyLB.ItemsSource = Binder.Instance.SCurrencyList;
Binding binding = new Binding();
binding.Mode = BindingMode.TwoWay;
binding.Source = settings;
binding.Path = new PropertyPath("STileCurrency");
sCurrencyLB.SetBinding(ListBox.SelectedItemProperty, binding);
When I try to set ItemSource in XAML and Binding in Codebehind , it doesn't work too

Using FindAncestor from within an Itemscontrol datatemplate to find a textblock outside of the datatemplate

I have a ItemsControl that's bound to an object, within the datatemplate of the ItemsControl i have two textblocks, I want to bind the first textblock's text property to another textblock that sits outside this ItemsControl.
I have tried finding the object in the parent datacontext and also simply trying to find the TextBlock with the Path=Text
one example is below :
<TextBlock Name="Name" Text="{Binding Name}"
Grid.Column="0"
FontSize="{DynamicResource SmallSize}"
TextWrapping="Wrap"
TextAlignment="Right"
Padding="4,0,0,0"
Grid.ColumnSpan="2" Background="Aqua"/>
<ItemsControl ItemsSource="{Binding TheValue}"
Padding="4,0,0,0"
Grid.Column="2"
HorizontalAlignment="Right">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text = "{
Binding RelativeSource =
{RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=Name}"
Grid.Column="0"
FontSize="{DynamicResource SmallSize}"
TextWrapping="Wrap" ........................
{Binding RelativeSource = {RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=Name}
Here you say to WPF find first parent of this control with Type Window, e.g. it's "ParentWindow". After this binding occurs to "ParentWindow" Name property.
If you want enable binding to control, which defined in same XAML, you can set the source explicitly by using Binding.ElementName property.
This is example for you code:
<TextBlock Text = "{Binding ElementName=Name, Path=Text}"/>
By the way, using control name as "Name" not is good. If you use this control form code behind it's looking as Name.Text = "some text", which can cause a trouble to understand what is going on.
UPDATE:
Example of binding to control DataContext Property in different datatemplate
class MainViewModel
{
public Class1 C1 { get; set; }
public Class2 C2 { get; set; }
public MainViewModel()
{
C1 = new Class1 { S1 = "This is C1 data context" };
C2 = new Class2 { S2 = "This is C2 data context" };
}
}
In XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:MainViewModel}">
<StackPanel>
<ContentControl Name="cc1" Content="{Binding C1}"/>
<ContentControl Name="cc2" Content="{Binding C2}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Class1}">
<TextBlock Text="{Binding S1}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Class2}">
<TextBlock Text="{Binding ElementName=cc1, Path=DataContext.C1.S1}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}"/>
</Grid>
</Window>
But, I don't think something like this is a good approach. Especially, because this can be many items with this DataTemplate. Maybe you need delegate this to your ViewModel.

ObservableCollection Images in Listbox to Content Control master detail WPf

I have an observablecollection of Images that get populated via the following code:
<StackPanel Orientation="Horizontal" Grid.Column="0">
<ListBox ItemsSource="{Binding BigImageView}" IsSynchronizedWithCurrentItem="True"
SelectedIndex="0" SelectedItem="{Binding CurrentItem}" />
</StackPanel>
<ContentControl Name="Detail" Content="{Binding BigImageView, Mode=OneWay}"
Margin="9,0,0,0" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"/>
However the Content Control is supposed to bind to the BigImageView via an ObservableCollection
BigImage = new ObservableCollection<Image>();
_listView = CollectionViewSource.GetDefaultView(BigImage);
_listView.CurrentChanged += new EventHandler(OnCurrentChanged);
public System.ComponentModel.ICollectionView BigImageView
{
get
{
return _listView;
}
set
{
_listView = value;
OnPropertyChanged("BigImageView");
}
}
I want to return the image to the content control when I move the listbox. I have been racking my brain and trying everyhitn but it does not work. any help would be appreciated.
There is no need to bind the selecteditem, the collectionview should take care of that.
Try this:
<ListBox ItemsSource="{Binding BigImageView}" IsSynchronizedWithCurrentItem="True" />
<ContentControl Name="Detail" Content="{Binding BigImageView, Mode=OneWay}" VerticalAlignment="Top">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
<ContentControl.ContentTemplate>
1
Create a viewmodel with a list and a selected item:
public class BigImageViewModel : INotifyPropertyChanged
{
private string bigImage;
//string for path?
public ObservableCollection<string> BigImageView {get; set; } //Of course, make sure it has a value
public string SelectedBigImage
{
get { return bigImage; }
set { bigImage = values; NotifyPropertyChanged("SelectedBigImage"); }
}
}
Set this object on the DataContext of your control in the constructor:
DataContext = new BigImage(); //Make sure you initialize your list
Set the ListBox ItemsSource to your BigImage list, bind your SelectedItem to BigImageView
and use that in your content control:
<ListBox ItemsSource="{Binding BigImageView}" SelectedItem={Binding SelectedBigImage} />
ContentControl:
<ContentControl Name="Detail" Content="{Binding SelectedBigImage, Mode=OneWay}" VerticalAlignment="Top">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/> <!-- Nice template for showing your string BigImage -->
</DataTemplate>
<ContentControl.ContentTemplate>
</ContentControl>
2
Or screw that view model:
Set the list directly in the constructor (after the InitializeComponent() ):
myListBox.ItemsSource = ObservableCollection<string>(); //Make sure you initialize your list with whatever your object is..
Give the list a name:
And bind with an ElementName binding to your selected item:
<ContentControl Name="Detail" Content="{Binding ElementName=myListBox, Path=SelectedItem}" VerticalAlignment="Top">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/> <!-- Nice template for showing your string BigImage -->
</DataTemplate>
<ContentControl.ContentTemplate>
</ContentControl>

How can I set my data binding in a WPF ItemsControl child to use a property on the parent data context?

I have a WPF ListView with a collection of RadioButtons. I want to set the GroupName of the child controls to be bound to a property on the parent data context. At the moment I am doing this by duplicating the property in each of the children's data context but that can't be right.
My XAML:
<ListView ItemsSource="{Binding OptionItems}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type Logging:FilterOptionsRadioListViewModel}">
<RadioButton GroupName="{Binding GroupName}" Content="{Binding Option.Value}" Tag="{Binding Option.Key}" IsChecked="{Binding IsChecked}" Command="Logging:FilterOptionsRadioListViewModel.CheckedChangedCommand" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
GroupName is a property on the parent View Model. I currently pass this onto the child View Model (where it is also a property) in the child constructor:
var item = new FilterOptionsRadioListItemViewModel(option, this.GroupName);
What is the correct way of doing this?
<ListView ItemsSource="{Binding OptionItems}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type Logging:FilterOptionsRadioListViewModel}">
<RadioButton GroupName="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.GroupName}" Content="{Binding Option.Value}" Tag="{Binding Option.Key}" IsChecked="{Binding IsChecked}" Command="Logging:FilterOptionsRadioListViewModel.CheckedChangedCommand" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You certainly can do that, but if you don't want to have to copy the GroupName to every child Option instance, you could instead do something like the following
// I'm imagining your viewmodels look something like this
public class ParentViewModel
{
private ObservableCollection<Option> m_OptionItems;
public ObservableCollection<Option> OptionItems
{
get { return m_OptionItems; }
set { m_OptionItems = value; }
}
private string m_ParentGroupName;
public string ParentGroupName
{
get { return m_ParentGroupName; }
set
{
m_ParentGroupName = value;
}
}
}
public class Option
{
private string m_Value;
public string Value
{
get { return m_Value; }
set
{
m_Value = value;
}
}
}
Then you can use a relative binding so you can look up the control tree to find the right datacontext, so you don't have to copy the ParentGroupName to each child Option instance.
If you name your window, you can traverse the root DataContext.
<Window x:Class="MyNamespace.MainWindow"
Name="Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="500" Width="725">
<ListView ItemsSource="{Binding OptionItems}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type Logging:FilterOptionsRadioListViewModel}">
<RadioButton GroupName="{Binding ElementName=Window, Path=DataContext.ParentGroupName}" Content="{Binding Option.Value}" Tag="{Binding Option.Key}" IsChecked="{Binding IsChecked}" Command="Logging:FilterOptionsRadioListViewModel.CheckedChangedCommand" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

TreeView Databinding

I want to add items in treeviewi n WPF.I have function as
public void SetTree(string Title,int Boxtype,int BoxNo )
{
sBoxType = "Group";
TreeList items = TreeList.Load(Title, sBoxType, BoxNo);
DataContext = items;
}
XAML Code of TreeView:
<TreeView Margin="16,275,18,312" x:Name="treeView1" ItemsSource="{Binding}" ItemTemplate="{StaticResource TreeItemTemplate}">
</TreeView>
<DataTemplate x:Key="TreeItemTemplate">
<WrapPanel>
<TextBlock Text="{Binding Path=Title}" />
<TextBlock Text="{Binding Path=Box}" />
</WrapPanel>
</DataTemplate>
Actually i wan to TreeView ot display lik
+Group (header)
Controllersgroup 5 (Child items).
As multicolumn child items.But it dislay like
Controllersgroup5
Instead of a regular DataTemplate you must use a HierarchicalDataTemplate and set it's ItemSource Property.
<HierarchicalDataTemplate ItemsSource="{Binding ChildItems}" />
like so.

Resources