in my Shell view XAML
<Grid>
....///
<StackPanel x:Name="SettingsPanelIsVisible" Grid.Row="6" Grid.Column="6" Orientation="Vertical"
VerticalAlignment="Bottom" >
<Button x:Name="Button1" Content="Button 1"/>
<Button x:Name="Button2" Content="Button 2"/>
</StackPanel>
</Grid>
and in my ShellViewModel
....//
public bool SettingsPanelIsVisible { get; set; }
as I understood these should be bound and the visibility should be toggled by caliburn.Micro by setting or resetting the bool, however this is not working, even though the other components are seeming to work fine
hum i just read StackPanel have some convention name, you error is maybe coming from your definition of viewmodel class ( dont see the code):
you have to put in viewmodel: using Caliburn.Micro;
public class ShellViewModel:Screen or PropertyChangedBase
{
private bool _SettingsPanelIsVisible;
public bool SettingsPanelIsVisible
{
get => _SettingsPanelIsVisible;
set
{
_SettingsPanelIsVisible = value;
NotifyOfPropertyChange(() => SettingsPanelIsVisible);
}
}
:
}
this convention is ok with Grid or StackPanel and maybe WrapPanel.....
Related
New to WPF here. The application being built has a list of users being pulled from a database for display in a "Users" Window, navigable from a "Main" Window. The list seems to be transferred to the code behind, but the list of users isn't displaying in the "Users" Window ListBox. Does anyone see why this isn't displaying? Many thanks in advance!
"Main" Window directing:
UsersViewModel Usersvm = new UsersViewModel();
Usersvm.Users = new List<UserViewModel>();
DbEntities db = new DbEntities();
var pulledUsers = db.uspGetUsers().ToList();
foreach (var result in pulledUsers)
{
var pulledUser = new UserViewModel
{
FirstName = result.FirstName,
LastName = result.LastName,
EMail = result.Email,
UserID = result.UserID,
Position = result.Position,
EndDate = result.EndDate,
};
Usersvm.Users.Add(pulledUser);
}
new UsersWindow(Usersvm).Show();
UsersWindow code behind:
public partial class UsersWindow : Window
{
public UsersWindow(UsersViewModel uvm)
{
InitializeComponent();
listboxUsers.ItemsSource = uvm.Users;
}
}
UsersWindow.xaml:
<Window x:Class="DbEntities.UsersWindow"
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:local="clr-namespace:DbEntities"
mc:Ignorable="d"
Title="UsersWindow" Height="Auto" Width="900">
<Window.Resources>
<Style x:Key="borderBase" TargetType="Border">
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
</Style>
</Window.Resources>
<StackPanel>
<TextBlock x:Name="textBlock" Height="21" Margin="0,0,161,0" TextWrapping="Wrap"
Text="Users Page" VerticalAlignment="Top" RenderTransformOrigin="1.022,0.409" HorizontalAlignment="Right" Width="344"/>
<Grid>
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="151*" />
<ColumnDefinition Width="95*" />
<ColumnDefinition Width="110*" />
<ColumnDefinition Width="351*" />
<ColumnDefinition Width="75*" />
<ColumnDefinition Width="110*" />
</Grid.ColumnDefinitions>
<Border Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="Last Name" />
</Border>
<Border Grid.Column="1" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="First Name" />
</Border>
<Border Grid.Column="2" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="Position" />
</Border>
<Border Grid.Column="3" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="Email" />
</Border>
<Border Grid.Column="4" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="End Date" />
</Border>
<Border Grid.Column="5" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" />
</Border>
<ListBox x:Name="listboxUsers" HorizontalAlignment="Center" Height="Auto" Margin="3,25,0,0" VerticalAlignment="Top" Width="889"
ItemsSource="{Binding Users}" Grid.ColumnSpan="6">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="LastNameColumn" />
</Grid.ColumnDefinitions>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding LastName}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding FirstName}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding Position}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding Email}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding EndDate}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<Button Content="Edit" x:Name="editButton" Click="editButton_Click"/>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</Grid>
</StackPanel>
</Window>
And finally, the UsersViewModel, with a list of the user contact information:
public partial class UsersViewModel : Window
{
public List<UserViewModel> Users { get; set; }
}
EDIT (Solved):
Ed Plunkett's comments and answer directly solved the original ListBox question, and using that input combined with ThyArtIsCode's, which was all neatly presented by Monty, the process is much more elegant. Thanks to all who replied - there's a ton of great learning material here.
I see a couple things wrong...
First, your ViewModel is inheriting Window. If there isn't a particular reason for this, get rid of it. If you want to notify UI of changes made to your collection (which should ideally be part of your view model), make the view model inherit INotifyPropertyChanged.
You are also binding to ListBox here:
ItemsSource="{Binding Users}"
AND setting the ItemsSource again here:
listboxUsers.ItemsSource = uvm.Users;
BAD! If you are binding in XAML, there's absolutely no need to set the ItemsSource again. Need to modify the collection? Do so with the collection directly.
Also, since you're new to WPF, I figured I'd add some suggestions that helped me when I first started learning:
If you want things to go quicker, add IsAsync=True to your ListBox binding. This will enable asynchronous binding (amazing, I know).
Virtualize the crap out of that ListBox (simply add following to ListBox):
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
And one last thing, though others suggested using an ObservableCollection, it also comes with a performance hit when using large data. Even if you don't intend to have large data, it always safer to use a BindingList anyway. In fact, ObservableCollection has an upper hand when working with smaller data sets.
They are much quicker and share many similar properties as the OC.
You've got a few things to fix here, but nothing very complicated. Just a lot of MVVM/XAML housekeeping stuff.
The way MVVM works in XAML is that your viewmodels don't know about your views --- ideally they don't know about any UI at all. To make that happen with stuff like message boxes and file open dialogs can involve some contortions, but we're not going there right now. Incidentally, you definitely don't want to derive a view model from Window -- that's a UI class, and it doesn't do anything that view models need a base class to do.
So your viewmodels have public properties, which you've got, but when those properties change, they should fire notifications off into the darkness. To do that, you implement INotifyPropertyChanged on your viewmodel, and you fire the PropertyChanged event when a property changes. The UI will subscribe to those notifications -- if your view model is the DataContext of the element whose property is bound (clear as mud -- more on that later).
When a viewmodel exposes a collection, it usually uses ObservableCollection, because that class fires notifications on add/remove/etc. List doesn't do that. ObservableCollection comes with some overhead from all the notification stuff, so don't just use it everywhere -- still use List when all you need is a List.
So UsersViewModel.Users needs to be of type ObservableCollection<UserViewModel><UserViewModel>, and when the collection is replaced, fire PropertyChanged.
private ObservableCollection<UserViewModel> _users =
new ObservableCollection<UserViewModel>();
ObservableCollection<UserViewModel> Users {
get { return _users; }
set {
_users = value;
// Implementations of this are everywhere on Google, very simple.
OnPropertyChanged("Users");
// Or in C#6
//PropertyChanged?.Invoke(new PropertyChangedEventArgs(nameof(Users)));
}
}
And of course make sure UserViewModel also implements INotifyPropertyChnaged and fires notifications when its own property values change.
Next, your XAML binding for ItemsSource on the ListBox is correct, but assigning a collection to that property in code behind will break it. A {Binding ...} in XAML isn't just an assignment: It creates an instance of the Binding class, which sits in the middle and manages all the notification event business I mentioned above. You can create bindings programmatically, but doing it in XAML is much simpler and in 99.5+% of cases does everything you need.
Most importantly, the window needs to know about your viewmodel. Make that happen by assigning an instance of UsersViewModel to the window's DataContext. The window's child controls will inherit that DataContext, and all bindings will be evaluated in that context.
public partial class UsersWindow : Window
{
public UsersWindow(UsersViewModel uvm)
{
InitializeComponent();
var vm = new UsersViewModel();
// initialize vm if needed
DataContext = vm;
}
}
You could have the window's creator pass in a UsersViewModel instance via the window's constructor as well.
OK try this.....
ViewModel....
class Base_ViewModel : INotifyPropertyChanged
{
public RelayCommand<UserViewModel> editButton_Click_Command { get; set; }
public Base_ViewModel()
{
editButton_Click_Command = new RelayCommand<UserViewModel>(OneditButton_Click_Command);
this.Users = new ObservableCollection<UserViewModel>();
this.Users.Add(new UserViewModel() { FirstName = "John", LastName = "Doe", EMail = "JohnDoe#yahoo.com", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" });
}
private ObservableCollection<UserViewModel> _Users;
public ObservableCollection<UserViewModel> Users
{
get { return _Users; }
set { _Users = value; NotifyPropertyChanged("Users"); }
}
private void OneditButton_Click_Command(UserViewModel obj)
{ // put a break-point here and you will see the data you want to Edit in obj
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
User Class.....
public class UserViewModel : INotifyPropertyChanged
{
private string _FirstName;
public string FirstName
{
get { return _FirstName; }
set { _FirstName = value; NotifyPropertyChanged("FirstName"); }
}
private string _LastName;
public string LastName
{
get { return _LastName; }
set { _LastName = value; NotifyPropertyChanged("LastName"); }
}
private string _EMail ;
public string EMail
{
get { return _EMail; }
set { _EMail = value; NotifyPropertyChanged("EMail"); }
}
private string _UserID;
public string UserID
{
get { return _UserID; }
set { _UserID = value; NotifyPropertyChanged("UserID"); }
}
private string _Position;
public string Position
{
get { return _Position; }
set { _Position = value; NotifyPropertyChanged("Position"); }
}
private string _EndDate;
public string EndDate
{
get { return _EndDate; }
set { _EndDate = value; NotifyPropertyChanged("EndDate"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
XAML.....
Set the Window x:Name....
<Window x:Name="Base_V"......
DataContext
<Window.DataContext>
<ViewModels:Base_ViewModel/>
</Window.DataContext>
And the rest of the View....
<Grid>
<DataGrid Name="DataGrid1" ItemsSource="{Binding Users}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.editButton_Click_Command, ElementName=Base_V}" CommandParameter="{Binding}">Edit</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
you should end up with something like this....
Update 1
In the constructor of the Base_ViewModel
this.Users.Add(new UserViewModel() { FirstName = "John", LastName = "Doe", EMail = "JohnDoe#yahoo.com", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" });
this.Users.Add(new UserViewModel() { FirstName = "Fred", LastName = "Doe", EMail = "FredDoe#yahoo.com", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" });
// empty record to allow the use to Add a new record
this.Users.Add(new UserViewModel());
When the user selects the Edit button for the empty record they are in effect simply filling in a blank record, Once they have filled that in, make sure to add another blank record to produce a new (empty row) in the DataGrid ....
I'm trying to add buttons to UserControl. I have to follow MVVM pattern. I have created a class, DeviceButton, with different set/gets and a constructor. In a viewmodel, used as datacontext, is the ObservableCollection and a get-method for the collection. I have bound the collection to a ItemsControl source and tried to add a template. I guess I'm missing something 'cause the buttons won't load.
The UserControl is added into a tab (using dragablz) which as well is a part of a ObservableCollection, also added at run time (this is working just fine). The idea is, that the tab has a list of buttons that has to be created at run time, where the list is fetched from a web service - so the buttons has to be added dynamically/programatically. Overview is just the first tab - a template for each tab (reflecting the fetched items) is being implemented when the buttons work. For now, I'm just adding a test button to the collection, but as stated, this won't show. What am I missing?
I have a Overview.xaml file:
<UserControl // attributes omitted to save spaces... ask if needed>
<StackPanel>
<Border>
<TextBlock FontSize="16" FontWeight="Bold" TextWrapping="WrapWithOverflow"
TextAlignment="Center" HorizontalAlignment="Center"
Foreground="{DynamicResource AccentColorBrush}">
Welcome to Greenhouse App
</TextBlock>
</Border>
<ItemsControl ItemsSource="{Binding DeviceButtons}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vmodel:DeviceButton}">
<Button Width="50" Height="50" Margin="10 10 0 0" Command="{Binding OpenTab}"
CommandParameter="{Binding DeviceType}" HorizontalAlignment="Left"
VerticalAlignment="Top" Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="{Binding ImageUrl}" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
// Manually added test button...
<Button x:Name="windMill" HorizontalAlignment="Left" Margin="10,0,0,0"
VerticalAlignment="Top" Width="50" Height="50"
Command="{Binding OpenTab}" FontFamily="Segoe Ui"
Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="/Greenhouse;component/Icons/Windmill.png" />
</StackPanel>
</Button
</StackPanel>
I'm trying to add an ObservableCollection of type DeviceButton, the _deviceButtons collection (Binded as ItemsSource to the ItemsControl)
The tabList items are working just find, and I can manually add more if needed (these will later be added through the OpenNewTab-command, which should be bound to the buttons)
The DeviceButton file:
public class DeviceButton
{
private readonly string _content;
private readonly string _deviceType;
private readonly string _imageUrl;
public DeviceButton(string content, string deviceType, string imageUrl)
{
_content = content;
_deviceType = deviceType;
_imageUrl = imageUrl;
}
public string Content
{
get { return _content; }
}
public string DeviceType
{
get { return _deviceType; }
}
public string ImageUrl
{
get { return _imageUrl; }
}
}
The collection is located in a viewmodel file, MainWindowViewModel.cs:
public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
{
public ICommand OpenTab { get; set; }
private string tabControl { get; set; } = "0";
private IInterTabClient _interTabClient;
private ObservableCollection<TabContent> _tabContents = new ObservableCollection<TabContent>();
private ObservableCollection<DeviceButton> _deviceButtons = new ObservableCollection<DeviceButton>();
public MainWindowViewModel()
{
_interTabClient = new MyInterTabClient();
DeviceButtons.Add(new DeviceButton("Windturbine", "windturbine", "/Greenhouse;component/Icons/Windmill.png"));
TabContents.Add(new TabContent("Overview", new Overview()));
OpenTab = new RelayCommand<object>(OpenNewTab);
}
private void OpenNewTab(object obj)
{
MessageBox.Show("Yo"); // Not yet implemented
RaisePropertyChanged(() => tabControl);
}
public ObservableCollection<TabContent> TabContents
{
get { return _tabContents; }
}
public ObservableCollection<DeviceButton> DeviceButtons
{
get { return _deviceButtons; }
}
public IInterTabClient InterTabClient
{
get { return _interTabClient; }
}
}
The buttons aren't loaded when I start the program (only the "test" button added manually in the WPF).
The UserControl, Overview, is considered a tab in another Controls.Metrowindow, MainWindow.xaml:
<Window.Resources>
<Style TargetType="{x:Type dragablz:TabablzControl}">
<Setter Property="CustomHeaderItemTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<ContentPresenter Margin="4" Content="{Binding Content}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<dragablz:TabablzControl SelectedIndex="{Binding tabControl}" ItemsSource="{Binding TabContents}" x:Name="InitialTabablzControl" Margin="4 0 4 4">
<dragablz:TabablzControl.InterTabController>
<dragablz:InterTabController InterTabClient="{Binding MyInterTabClient}" />
</dragablz:TabablzControl.InterTabController>
</dragablz:TabablzControl>
I guess it's an issue with the resources/binding in the Overview.xaml, but I've exhausted all suggested solutions I could find.
Issue was in binding
ItemsSource="{Binding Path=DataContext.TabContents,
RelativeSource={RelativeSource AncestorLevel=1,
AncestorType={x:Type Window}, Mode=FindAncestor}}"
I suspect the DataContext is being set to a TabContent object, which does not have a DeviceButtons property
I would suggest using a tool like Snoop to verify your DataContext is what you expect.
Hey Embarrassingly I have not been able to get two-way binding working from the examples I have found. Here is one of my dataTemplates.
<local:ChatRoomTemplateSelector.YourSelf>
<DataTemplate x:Name="YourSelf">
<StackPanel>
<Grid x:Name="YourSelfGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<UserControl Grid.Row="0">
<Path Data="" Fill="LawnGreen" StrokeThickness="0" Stretch="Fill" UseLayoutRounding="True"/>
</UserControl>
<TextBox Text="{Binding Path=Post}" IsReadOnly="{Binding readOnly}" FontSize="20" Margin="39,10" TextWrapping="Wrap" TextAlignment="Center"/>
</Grid>
<StackPanel Orientation="Horizontal">
<Path Data="" Fill="LawnGreen" StrokeThickness="0" Stretch="Fill" UseLayoutRounding="True" Margin="50,-1,0,0"/>
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="25" TextWrapping="Wrap"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</local:ChatRoomTemplateSelector.YourSelf>
The element I need to have two-way binding on is Text="{Binding Path=Post}". But cannot get it working. The longlistselector I have, has the itemsource set to:
System.Collections.ObjectModel.ObservableCollection<Data> source = new System.Collections.ObjectModel.ObservableCollection<Data>();
This source is then set to my Longlistselectors itemsource.
Where the Data class is:
public class Data : INotifyPropertyChanged
{
public string Name
{
get;
set;
}
private string _dl;
public string Post
{
get { return _dl; }
set
{
if (value != _dl)
{
_dl = value;
NotifyPropertyChanged("Post");
}
}
}
public string User
{
get;
set;
}
public bool readOnly
{
get;
set;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
This is What I have tried with no success. Hope you can help me with the tweak that gives me two-way binding for the Post, in Data.
Extra Info
I now tried the mode=TwoWay, and finally got that working. Code updated.
But now the update is only happening upon lostfocus. But I need the update of the variable to happen when text is inserted.
So I need to set UpdateSourceTrigger.
But what should this be set to?
Solution
<TextBox TextChanged="OnTextBoxTextChanged"
Text="{Binding MyText, Mode=TwoWay,
UpdateSourceTrigger=Explicit}" />
UpdateSourceTrigger = Explicit is a smart bonus here. What is it? Explicit: Updates the binding source only when you call the UpdateSource method. It saves you one extra binding set when the user leaves the TextBox.
In C#:
private void OnTextBoxTextChanged( object sender, TextChangedEventArgs e )
{
TextBox textBox = sender as TextBox;
// Update the binding source
BindingExpression bindingExpr = textBox.GetBindingExpression( TextBox.TextProperty );
bindingExpr.UpdateSource();
}:
If you want your TextBox to be two way binded, then you should use Text="{Binding Path=Post,Mode=TwoWay}"
Windows phone don't support UpdateSourceTrigger=PropertyChanged but you can find a way to do that here .
Or an alternative is to use the UpdateTextBindingOnPropertyChanged from the Prism library. You can just download the source code of the file I linked and set it in in xaml like this:
<TextBox ...>
<i:Interaction.Behaviors>
<prism:UpdateTextBindingOnPropertyChanged/>
</i:Interaction.Behaviors>
</TextBox>
I am not able to get the UI to respond to RaisePropertyChanged for some reason. Well stuck.
CurrentClient is the part not firing to the UI. Really strange.
Hopefully someone can help me out. Thanks Scott
public class ClientViewModel : ViewModelBase
{
public RelayCommand<ClientDetail> SelectedClient { get; private set; }
public ICollectionView ClientView { get; private set; }
private readonly IClients _clientsService;
public ClientViewModel(IClients clientsService)
{
_clientsService = clientsService;
SelectedClient = new RelayCommand<ClientDetail>(InSelectedClient);
ClientDetailsList = new ObservableCollection<ClientDetail>(_clientsService.LoadClientList());
//CurrentClient = ClientDetailsList.ToList().FirstOrDefault();
ClientView = CollectionViewSource.GetDefaultView(ClientDetailsList);
}
private void InSelectedClient(ClientDetail obj)
{
CurrentClient = obj as ClientDetail;
}
private ObservableCollection<ClientDetail> _clientDetailsList;
public ObservableCollection<ClientDetail> ClientDetailsList
{
get { return _clientDetailsList; }
set { _clientDetailsList = value;
RaisePropertyChanged("ClientDetailsList"); }
}
private ClientDetail _currentClient;
public ClientDetail CurrentClient
{
get { return _currentClient; }
set
{
if (_currentClient == value)
{ return; }
_currentClient = value;
RaisePropertyChanged("CurrentClient");
}
}
}
My XAML :
<ListBox Name="lbClientList"
ItemsSource="{Binding ClientView}" ItemTemplate="{DynamicResource DTClientList}"
Background="{x:Null}" BorderThickness="0,0,1,0" BorderBrush="#FF434343" >
<ListBox.Resources>
<DataTemplate x:Key="DTClientList">
<StackPanel Orientation="Horizontal">
<TextBlock TextWrapping="Wrap" Text="{Binding CM_CompanyID}" Margin="0,0,5,0" Width="25" Foreground="#FF3590FC"/>
<TextBlock TextWrapping="Wrap" Text="{Binding CM_CompanyName}" Width="150" Foreground="#FF3590FC"/>
<TextBlock TextWrapping="Wrap" Text="{Binding CM_MainContact}" Width="100" Foreground="#FF3590FC"/>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<Command:EventToCommand Command="{Binding SelectedClient, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=lbClientList}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.DataContext>
<Binding Path="ClientView" Source="{StaticResource ServiceLocator}" UpdateSourceTrigger="PropertyChanged"/>
</ListBox.DataContext>
</ListBox>
In debug the code is hitting RaiseProertyChange but I am seeing nothing on my UI
<TextBox Text="{Binding CurrentClient.CM_Address1}" TextWrapping="Wrap" VerticalAlignment="Center" Width="210" Background="{DynamicResource MainBackgrouund}" BorderThickness="0,0,0,1" >
Ok not really sure about your code structure since I cannot reproduce your issue with the current info.
However couple things that might be worth you knowing about,
In InSelectedClient(...) the argument is already of type ClientDetail making the as cast redundant inside the function.
Next why are you even using a EventToCommand for this? you hold the ListBox selected item via the EventToCommand in CurrentClient. Rather just bind it directly.
something like:
<ListBox ...
SelectedItem="{Binding CurrentClient}">
...
Finally If you don't have any specific logic in the VM relating to CurrentClient and if there is really no necessity holding on to it, then you can get rid of it by binding your TextBox directly to the ListBox.
something like
<TextBox Text="{Binding ElementName=lbClientList, Path=SelectedItem.CM_Address1}" />
I'm guessing CM_Address1 is a "property" in ClientDetail class
Now all these approaches work fine for me. I'd suggest putting together a reproducible stand-alone example if none of these work for you. I can attach a sample of these methods in a demo if you want(not really sure it's gonna be of much help if your code is structured well different)
I have 3 models: Machine, Part, and Component. On my ui, I have bound one listbox to a list of Machines and a second listbox to the selected Machine's list of Parts. Now when the user selects a part, I would like to bind a datagrid to the selected part's list of components. I tried setting the datagrid's itemssource to:
ItemsSource="{Binding ElementName=PartSelectList, Path=SelectedItem.Components}"
I also tried setting its datacontext and itemssource
DataContext={Binding ElementName=PartSelectList, Path=SelectedItem}
ItemsSource={Binding Components}
However the datagrid does not automatically update if changes are made to the part's list of components via methods such as
part.Bags.Add(new Component(..));
Anyone have suggestions on how I should approach this?
Update: I have a class MachineCollection which implements INotifyPropertyChanged. In my xaml.cs code I create my MachineCollection object and bind the MainGrid to it's Machines property:
Page1.xaml.cs:
private MachineCollection machines
public Page1()
{
machineCollection = new MachineCollection();
MainGrid.DataContext = machineCollection.Machines
actionThread = new Thread(InitLists);
actionThread.Start();
}
public void InitLists()
{
machineCollection.Machines.AddRange(dbCon.GetAllMachines());
}
Classes:
public class MachineCollection : INotifyPropertyChanged
{
public List<Machine> Machines
{
get { return machines; }
set { machines = value; NotifyPropertyChanged("Machines"); }
}
}
public class Machine : INotifyPropertyChanged
{
public List<Part> Parts
{
get { return parts; }
set { parts = value; NotifyPropertyChanged("Parts");
}
}
public class Part : INotifyPropertyChanged
{
public List<Component> Components
{
get { return components; }
set { components = value; NotifyPropertyChanged("Components");
}
}
public class Component : INotifyPropertyChanged
{
public int Length
{
get { return length; }
set { length = value; NotifyPropertyChanged("Length");
}
}
XAML:
<Page>
<Grid Name="MainGrid">
<ListBox x:Name="MachineSelectList" HorizontalAlignment="Left" VerticalAlignment="Bottom"
Height="114" Width="231"
Foreground="White" Background="#FF7A7A7A" FontSize="16" BorderBrush="#FFC13131"
ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="200">
<TextBlock Text="{Binding SerialNumber}" TextAlignment="Left" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<Button Name="DeleteMachine" Tag="{Binding SerialNumber}" HorizontalAlignment="Right" VerticalAlignment="Center" Height="20"
Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}" Focusable="False" Click="Btn_Click" >
<Image Source="..\Resources\Images\delete.png" Stretch="Uniform"/>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Name="PartSelectList" HorizontalAlignment="Left" VerticalAlignment="Bottom"
Height="164" Width="231"
Background="#FF7A7A7A" Foreground="White" FontSize="16" BorderBrush="#FFC13131"
ItemsSource="{Binding Path=Parts}" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="200">
<TextBlock Text="{Binding PartNumber}" TextAlignment="Left" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<Button Name="DeletePart" Tag="{Binding PartNumber}" HorizontalAlignment="Right" VerticalAlignment="Center" Height="20"
Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}" Focusable="False" Click="Btn_Click" >
<Image Source="..\Resources\Images\delete.png" Stretch="Uniform"/>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Page>
One way to solve this is using ObservableCollection, which is made for situations like this.
public ObservableCollection<Part> Parts
{
get { return parts; }
set { parts = value; NotifyPropertyChanged("Parts");
}
Or you could implement the interface INotifyCollectionChanged manually
The problem is, basically, that the binding system does not know when you call Add. If you were reassigning the Parts property each time, then the system would just redraw everything, so it would reflect the changes too, but if ObservableCollection is not a problem, that's the prefered way