WPF ItemsControl binding issue - c#

I have problem with Binding: see my code
This is Xaml code:
<ItemsControl x:Name="lbOpenInvoices" ItemsSource="{Binding Path=ocOpenInvoices}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" VerticalAlignment="Top" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="btnOpenInvoice" Click="btnOpenInvoice_Click" Style="{StaticResource OpenInvoicesButton}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Converter={StaticResource InvoiceNoTableNo}}"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="{Binding Converter={StaticResource InvoiceNoInvoiceId}}"/>
<TextBlock Text="{Binding TotalAmount}" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
<TextBlock Text="{Binding Converter={StaticResource InvoiceDateTime}}"/>
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
In Code behind I have declared the ocOpenInvoices ObservableCollection:
public ObservableCollection<Invoice> ocOpenInvoices { get; set; }
And In my Window Loadded event:
void SaleWindow_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this;
}
But It driving me Crazy because the ItemControl does not respond to ocOpenInvoices ObservableCollection.
When I give it the ItemsSource from codebehind it works :(, I have tried to give it the ElementName but it still not respond.
Please can you help and tell me what's my problem? what I miss here?
Thanks in advance.

Try abstracting your observable collection over a private variable a private, it will work.
Replace
public ObservableCollection<Invoice> ocOpenInvoices { get; set; }
with
private ObservableCollection<Invoice> _ocOpenInvoices;
public ObservableCollection<Invoice> ocOpenInvoices
{
get { return _ocOpenInvoices ; }
set { _ocOpenInvoices = value; OnPropertyChange("ocOpenInvoices"); }
}
Please ignore this OnPropertyChange, if you have already implemented INotifyPropertyChanged in your own way, otherwise, it would be INotifyPropertyChanged that can solve your issue.

Make sure you initialize your ObservableCollection within Window's constructor or Window loaded event.
void SaleWindow_Loaded(object sender, RoutedEventArgs e)
{
ocOpenInvoices = new ObservableCollection<Invoice>();
this.DataContext = this;
}
If you are initializing it somewhere else than make sure you implement INotifyPropertyChanged and raise PropertyChanged.

Related

Force ListView to refresh or Invalidate in UWP

I have a ListView with ItemTemplate. I want to bind one control background in ItemTemplate to 2 properties, one of properties is in ItemsSource and onother one is in my page. since UWP has no multibinding support, I bind it to one property in ItemSource and for another property in my page I want to handle it in my code behind.
<ListView >
<ListView.ItemTemplate>
<DataTemplate>
<Border HorizontalAlignment="Stretch"
x:Name="myborder"
Padding="5,0,5,0"
Background="{Binding myProperty, Converter={StaticResource convertPropertyToBgColor },ConverterParameter=border}">
<StackPanel Padding="0,10,10,10"
Background="{Binding myProperty, Converter={StaticResource convertPropertyToBgColor},ConverterParameter=stack}">
<TextBlock Text="{Binding Text}">
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
in the convertPropertyToBgColor I get the brush from Resources.
and in code behind when my second desired property is changed I Change My resources. so the brush I have used from resources get changed and because of that I want to call that converter again to refresh Background, I called updateLayout but it doesn't refresh my ListView and it doesn't call myConvereter again. How can I force ListView to recreate or refresh Items that it has made?
Generally you class should implement INotifyPropertyChanged, then once you change the property, usually in its setter you also call OnPropertyChanged event which will update your UI. There are plenty examples of that, here is one.
The other way, may be to call Bindings.Update(), but normally you probably should use the method above.
To make my comments clearer - something like this is possible:
<StackPanel Orientation="Horizontal" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="myList" Width="200">
<ListView.ItemTemplate>
<DataTemplate>
<Border HorizontalAlignment="Stretch"
x:Name="myborder"
Padding="5,0,5,0"
Background="{Binding Path=DataContext.MyProperty, ElementName=myList}">
<StackPanel Padding="0,10,10,10">
<TextBlock Text="{Binding}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
<x:String>Element 1</x:String>
<x:String>Element 2</x:String>
<x:String>Element 3</x:String>
</ListView>
<Button Content="Change" Click="Button_Click"/>
</StackPanel>
code behind:
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private SolidColorBrush myPropety = new SolidColorBrush(Colors.Red);
public SolidColorBrush MyProperty
{
get { return myPropety; }
set { myPropety = value; RaiseProperty(nameof(MyProperty)); }
}
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e) => MyProperty = new SolidColorBrush(Colors.Blue);
}

Binding to TextBlock in ListView

I don't know if my understanding of binding is just poor or if I am not seeing the problem, but I hope someone can help me out here. I have a ListView with a template of an image and a TextBlock, I need the TextBlock to be bound to the ItemsSource of the ListView. However when I run this I get nothing shown, I don't even see my image that I have set.
XAML:
<UserControl.Resources>
<FontFamily x:Key="FontFamily">MS Reference Sans Serif</FontFamily>
</UserControl.Resources>
<Grid>
<ListView BorderThickness="0" ItemsSource="{Binding Facies}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="../Images/Shale.png"/>
<TextBlock Text="{Binding FaciesName}" Width="75" Margin="5"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
C#:
public partial class FaciesControl : UserControl
{
public FaciesControl()
{
InitializeComponent();
}
public List<string> Facies {get; set;}
public void Bind(string[] data)
{
Facies = new List<string>();
Facies.AddRange(data);
}
}
First set DataContext like this:
public FaciesControl()
{
InitializeComponent();
string[] str = { "Name1", "Name2", "Name3" };
Bind(str); // Make sure you have called the Bind method
DataContext = Facies;
}
Second change your XAML like this:
<ListView BorderThickness="0" ItemsSource="{Binding}">
....
....
<TextBlock Text="{Binding}" Width="75" Margin="5"/>

Windows Phone - custom control how propagate event to ViewModel

I am creating custom control for menu and I have ListBox in my control. Something like this:
<ListBox x:Name="MenuItemsList"
Grid.Row="1"
ItemsSource="{Binding ProgramList, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now when I want to catch Tap event, get properties from class and keep MVVM model. How can I catch this in MainPage.xaml.cs or in MainViewModel?
I have this code in my MainPage.xaml:
<controls:BottomMenu x:Name="BottomMenu" Canvas.Top="{Binding MenuCanvasTop}"
Width="480" Height="400">
</controls:BottomMenu>
I have prepare this code in my MainViewModel:
public RelayCommand<string> GoToSectionCommand
{
get
{
return goToArticleCommand
?? (goToArticleCommand = new RelayCommand<string>(
NavigateToSection));
}
}
But I don't know how can I call it. What's the best way?
Edit:
I tried to extend listbox:
<ListBox x:Name="MenuItemsList"
Grid.Row="1"
ItemsSource="{Binding ProgramList, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<Button Content="{Binding Title}"
Command="{Binding ListButtonClickCommand, Source={RelativeSource Self}}"
CommandParameter="{Binding Url}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With code-behind:
public ICommand ListButtonClickCommand
{
get { return (ICommand)GetValue(ListButtonClickCommandProperty); }
set { SetValue(ListButtonClickCommandProperty, value); }
}
public static readonly DependencyProperty ListButtonClickCommandProperty =
DependencyProperty.Register("ListButtonClickCommand", typeof(ICommand), typeof(BottomMenu), new PropertyMetadata(null));
public BottomMenu()
{
InitializeComponent();
}
Then in MainPage.xaml:
<controls:BottomMenu x:Name="BottomMenu" Canvas.Top="{Binding MenuCanvasTop}"
Width="480" Height="400"
ListButtonClickCommand="{Binding MenuItemButtonCommand}">
</controls:BottomMenu>
And in MainViewModel:
private ICommand menuItemButtonCommand;
public ICommand MenuItemButtonCommand
{
get
{
return menuItemButtonCommand
?? (menuItemButtonCommand = new RelayCommand(
NavigateToArticleSection));
}
}
For now without luck. It's not working. RelayCommand isn't triggered.
Edit2
I guess the problem is with binding command in custom control but I don't know how to fix it.
You just need to bind to the UserControl -- using "RelativeSource Self" will bind to the Button, which is not what you want. You should be able to use an "ElementName" binding to locate the user control:
<UserControl x:Name="UserControlName" ... >
...
<Button Content="{Binding Title}"
Command="{Binding ElementName=UserControlName,Path=ListButtonClickCommand}"
CommandParameter="{Binding Url}"/>
...
</UserControl>

combobox with checkbox within a WPF DataGrid

I need to display combobox with checkbox option within a DataGrid in WPF. Please provide any solution for that.
I have tried the below code
<toolkit:DataGridTemplateColumn Header="Template">
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox>
<ComboBoxItem BindingGroup="{Binding Program}">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}" Width="20" />
<TextBlock Text="{Binding Program}" Width="100" />
</StackPanel>
</ComboBoxItem>
</ComboBox>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
It will output like this
Anyone please help to load collection of items in combobox and to correct my code.
CS code:
private void resultGrid_Loaded(object sender, RoutedEventArgs e)
{
var programs = new List<Programs>();
programs.Add(new Programs("test", false));
programs.Add(new Programs("test1", false));
programs.Add(new Programs("test2", true));
//var grid = sender as DataGrid;
resultGrid.ItemsSource = programs;
Combo.ItemsSource = programs;
}
And the Model:
public class Programs
{
public Programs(string Program, bool IsChecked)
{
this.Program = Program;
this.IsChecked = IsChecked;
}
public string Program { get; set; }
public bool IsChecked { get; set; }
}
Finaly got an idea #Sheridan mentioned :
You provided a DataTemplate to define that your column should render a ComboBox, so I'm not really sure why you can't just extend that and provide a DataTemplate to define that your ComboBoxItems should render as Checkboxes. Try something like this:
<toolkit:DataGridTemplateColumn Header="Template">
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}" Width="20" />
<TextBlock Text="{Binding Program}" Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
I'll leave you to finish this off.

MVVM Light RaisePropertyChanged

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)

Categories