I have a wpf with multiple tabs. The application is a step by step learning tool for students. I want the tabs to be locked initially. When the user has entered the correct data in a tab, the next is enabled after pressing the proceed button.
I tried binding the 'isEnabled' property of tabitem and setting it true when the button is pressed in the View model. It doesn't work.
Here's some snippets of my code:
XAML:
<TabItem Header="Step 1"><my:Step1 Loaded="Step1_Loaded" /></TabItem>
<TabItem Header="Step 2"><my:Step2 Loaded="Step2_Loaded" /></TabItem>
<TabItem IsEnabled="{Binding step3Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Step 3"><my:Step3 Loaded="Step3_Loaded" /></TabItem>
Code-behind for the button:
private void proceed2_Click(object sender, RoutedEventArgs e)
{
var vm = DataContext as ViewModel.ValuesCheck;
if (vm != null)
vm.Step2check();
}
View Model:
class ViewModel:INotifyPropertyChanged
{
bool _step3enabled = false;
public bool step3Enabled
{
get { return _step3enabled; }
set { _step3enabled = value; OnPropertyChanged("step3Enabled"); }
}
public void Step2check()
{
this.step3Enabled = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
}
As I can understand, you need some navigation control that presents its content based on some condition. I can suggest you to use a listbox control and manage its ListBoxItems content visibility or something like this.
Xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding Controls}" SelectedItem="{Binding CurrentControlContent}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Visibility="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource Converter}}"
BorderBrush="Tomato" BorderThickness="1" IsEnabled="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<Button Width="100" Height="30" IsEnabled="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
IsHitTestVisible="False" Content="{Binding Path = Content, UpdateSourceTrigger=PropertyChanged}"></Button>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ContentControl Content="{Binding CurrentControlContent.Content}"></ContentControl>
<Button Grid.Row="1" Content="Check" Command="{Binding CheckCommand}"></Button>
</Grid>
ViewModel
private TabAndControlModel _selectedTabControlModel;
private ICommand _checkCommand;
private TabAndControlModel _currentControlContent;
public ObservableCollection<TabAndControlModel> Controls { get; set; }
public TabAndControlModel CurrentControlContent
{
get { return _currentControlContent; }
set
{
_currentControlContent = value;
OnPropertyChanged();
}
}
public ICommand CheckCommand
{
get { return _checkCommand ?? (_checkCommand = new RelayCommand(Check)); }
}
private void Check()
{
var index = Controls.IndexOf(CurrentControlContent);
var nextIndex = index + 1;
if(nextIndex >= Controls.Count) return;
CurrentControlContent = Controls[nextIndex];
CurrentControlContent.TabIsEnabled = true;
}
Model
private bool _tabIsEnabled;
private string _content;
public bool TabIsEnabled
{
get { return _tabIsEnabled; }
set
{
_tabIsEnabled = value;
OnPropertyChanged();
}
}
public string Content
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged();
}
}
View on running:
And you have to template and style all that stuff...
Regards
Related
All, I am having a lot of problems with displaying a custom tooltip for my LiveCharts control Here is my code and what I have tried.
Chart C# Code:
public partial class Chart : UserControl
{
public ChartData chartData { get; set; }
public Chart()
{
chartData = new ChartData();
InitializeComponent();
Loaded += Control_Loaded;
}
private void Control_Loaded(object sender, RoutedEventArgs e)
{
// build here
DataContext = this;
chartData.Formatter = value => value.ToString("C", CultureInfo.CurrentCulture);
}
}
Chart XAML:
<UserControl x:Class="LandlordTenantDatabaseWPF.Chart"
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:local="clr-namespace:LandlordTenantDatabaseWPF"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:Chart}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="txtTitle" Grid.Row="0" Text="{Binding chartData.Title}" Foreground="#FF4B52E4" FontSize="36" TextAlignment="Center"/>
<lvc:CartesianChart x:Name="chart" Grid.Row="1" Series="{Binding chartData.SeriesCollection}" LegendLocation="Right" >
<lvc:CartesianChart.AxisX>
<lvc:Axis Title="Month" Labels="{Binding chartData.Labels}"></lvc:Axis>
</lvc:CartesianChart.AxisX>
<lvc:CartesianChart.AxisY>
<lvc:Axis Title="Payments" LabelFormatter="{Binding chartData.Formatter}"></lvc:Axis>
</lvc:CartesianChart.AxisY>
<lvc:CartesianChart.DataTooltip>
<local:CustomersTooltip/>
</lvc:CartesianChart.DataTooltip>
</lvc:CartesianChart>
</Grid>
ToolTip C# Code:
public partial class CustomersTooltip : IChartTooltip
{
private TooltipData _data;
public CustomersTooltip()
{
InitializeComponent();
//LiveCharts will inject the tooltip data in the Data property
//your job is only to display this data as required
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public TooltipData Data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged("Data");
}
}
public TooltipSelectionMode? SelectionMode { get; set; }
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ToolTip XAML:
<UserControl x:Class="LandlordTenantDatabaseWPF.CustomersTooltip"
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:wpf="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
xmlns:local="clr-namespace:LandlordTenantDatabaseWPF"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
d:DataContext="{d:DesignInstance local:CustomersTooltip}"
Background="#E4555555" Padding="20 10" BorderThickness="2" BorderBrush="#555555">
<ItemsControl ItemsSource="{Binding Data.Points}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type wpf:DataPointViewModel}">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="Title"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LastName"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="Phone"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="PurchasedItems"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Stroke="{Binding Series.Stroke}" Fill="{Binding Series.Fill}"
Height="15" Width="15"></Rectangle>
<TextBlock Grid.Column="1" Text="{Binding ChartPoint.Instance.(local:CustomerVm.Name)}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
<TextBlock Grid.Column="2" Text="{Binding ChartPoint.Instance.(local:CustomerVm.LastName)}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
<TextBlock Grid.Column="3" Text="{Binding ChartPoint.Instance.(local:CustomerVm.Phone),
StringFormat=Phone: {0}}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
<TextBlock Grid.Column="4" Text="{Binding ChartPoint.Instance.(local:CustomerVm.PurchasedItems),
StringFormat=Purchased Items: {0:N}}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ChartData Class:
public class ChartData : INotifyPropertyChanged
{
private SeriesCollection seriesCollection;
public SeriesCollection SeriesCollection
{
get { return seriesCollection; }
set
{
if (seriesCollection != value)
{
seriesCollection = value;
OnPropertyChanged("seriesCollection");
}
}
}
private string[] labels;
public string[] Labels
{
get { return labels; }
set
{
if(labels != value)
{
labels = value;
OnPropertyChanged("labels");
}
}
}
private string title;
public string Title
{
get { return title; }
set
{
if(title != value)
{
title = value;
OnPropertyChanged("title");
}
}
}
private Func<double, string> formatter;
public Func<double, string> Formatter
{
get { return formatter; }
set
{
if(formatter != value)
{
formatter = value;
OnPropertyChanged("formatter");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And the Window Containing the Chart:
private void AddNewSeries(List<double> lstPayments, string sSeriesName, SolidColorBrush colorBrush)
{
if (m_SeriesCollection == null)
{
m_SeriesCollection = new SeriesCollection
{ new ColumnSeries
{
Values = new ChartValues<double>(lstPayments),
Title = sSeriesName,
Fill = colorBrush
}
};
}
else
{
m_SeriesCollection.Add(
new ColumnSeries
{
Values = new ChartValues<double>(lstPayments),
Title = sSeriesName,
Fill = colorBrush
}
);
}
}
private void UpdateChartsBasedOnDates(Chart chart, string sChartTitle)
{ //Pass data to the chart
chart.chartData.SeriesCollection = m_SeriesCollection;
chart.chartData.Labels = GetFormattedDates(m_lstCombinedDates);
chart.chartData.Title = sChartTitle;
}
This all works perfectly when I just allow the default tooltip to be displayed on the chart. I am hung up on the fact that the chart data is supplied by a SeriesCollection, but the custom ToolTip, in the example on the LiveCharts website https://lvcharts.net/App/examples/v1/wpf/Tooltips%20and%20Legends doesn't use the SeriesCollection. Some of the charts displayed by my custom chart control have more than 1 series. I tried binding the Tooltip control to the SeriesCollection to get the data values, but that didn't work. Granted I may have done it incorrectly. Obviously the ToolTip XAML I am showing here is from the example code on the LiveCharts website, but I don't know where to go from here.
Is it possible to use make the tooltip use the series collection? What would be the best way for me to either use the Series Collection, or the ChartData class, or can I use
public TooltipData Data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged("Data");
}
}
from the ToolTip C# code to display the data in the ToolTip?
I'm so confused.
Edit: I forgot to mention what my goal is. I want to display a tooltip that says
string.Format("{0} Payment: ${1}", SeriesName, PaymentAmount);
This would display Rent Payment: $1000. or MortgagePayment: $1000.00
Without being a deep LiveCharts Tooltip Wiz I did manage to get a version going that worked for me. My chart is also using a SeriesCollection, so maybe this can be helpful for you.
This is how I integrated it into my chart:
CartesianChart cartesianChart = ...;
_cartesianChart.DataTooltip = new CustomTooltip
{
SelectionMode = TooltipSelectionMode.OnlySender
};
This is my CustomToolTip class (I believe this is pretty much straight out of the documentation somewhere):
public partial class CustomTooltip : UserControl, IChartTooltip
{
private TooltipData _data;
public CustomTooltip()
{
InitializeComponent();
//LiveCharts will inject the tooltip data in the Data property
//your job is only to display this data as required
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public TooltipData Data
{
get => _data;
set
{
_data = value;
OnPropertyChanged("Data");
}
}
public TooltipSelectionMode? SelectionMode { get; set; }
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class CustomTooltip : UserControl, IChartTooltip, INotifyPropertyChanged
{
private bool _contentLoaded;
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
public void InitializeComponent()
{
if (_contentLoaded)
return;
System.Uri resourceLocater = new System.Uri(
"/Plotting/CustomTooltipDesign.xaml",
System.UriKind.Relative
);
System.Windows.Application.LoadComponent(this, resourceLocater);
_contentLoaded = true;
}
}
Then I built a XAML component, just like you. First super simple:
public partial class CustomTooltipDesign : UserControl
{
public CustomTooltipDesign()
{
DataContext = this;
}
}
Then I had a little more effort to build the actual tooltip:
<UserControl x:Class="expomrf4utility.CustomTooltipDesign"
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:expomrf4utility="clr-namespace:ExpoMRF4Utility"
xmlns:wpf="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance expomrf4utility:CustomTooltip}"
x:Name="Control" d:DesignWidth="1" d:DesignHeight="1">
<UserControl.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{Binding Foreground}"></Setter>
</Style>
<wpf:SharedConverter x:Key="SharedConverter"/>
<wpf:SharedVisibilityConverter x:Key="SharedVisibilityConverter"/>
<wpf:ChartPointLabelConverter x:Key="ChartPointLabelConverter"/>
<wpf:ParticipationVisibilityConverter x:Key="ParticipationVisibilityConverter"/>
<BooleanToVisibilityConverter x:Key="Bvc"></BooleanToVisibilityConverter>
<CollectionViewSource x:Key="GroupedPoints" Source="{Binding Data.Points}"/>
</UserControl.Resources>
<UserControl.Template>
<ControlTemplate>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" >
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding Source={StaticResource GroupedPoints}}" Grid.IsSharedSizeScope="True">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Name}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type wpf:DataPointViewModel}">
<Grid Margin="2" VerticalAlignment="Center" HorizontalAlignment="Center" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" SharedSizeGroup="Title"/>
<RowDefinition Height="Auto" SharedSizeGroup="Label"/>
<RowDefinition Height="Auto" SharedSizeGroup="Label"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="1" Text="{Binding Series.Title}" HorizontalAlignment="Left" FontWeight="Bold"/>
<TextBlock Grid.Row="2" Text="{Binding ChartPoint.Y, StringFormat='Value: {0} V/m'}" HorizontalAlignment="Left"/>
<TextBlock Grid.Row="3" Text="{Binding ChartPoint, Converter={StaticResource ChartPointLabelConverter}, StringFormat='Datetime: {0}'}" HorizontalAlignment="Left"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</ControlTemplate>
</UserControl.Template>
Note how I had to create bindings for the different properties that I wanted to display. To that end I slightly cheated when creating the data sets themselves. I used GLineSeries but I have a feeling it should work with other series as well:
int bandIndex = ...;
GearedValues<double> gearedValues = (some double[] data).AsGearedValues();
series.Add(new GLineSeries
{
Values = gearedValues,
LabelPoint = chartPoint => loggerData.Labels[(int)chartPoint.X].ToString(),
Tag = bandIndex,
Title = bandIndex.ToString()
});
Note that I chartPoint.X is the (zero-based) index of the the specific point (in X) - whereas chartPoint.Y is the Y-value of the point (also used in the XAML). Using this index I can look up in my list of names loggerData.Labels and return the timestamp associated with this index.
I have a user list in MainWindow. After pressing the preview button, a non-modal window for data editing opens. Updated data are changed in real time in the main window. The question is how to bind the windows so that because the user changes from the list in the main window, he changes in real time in an already open non-modal window.
WPF does not recommend coding business logic directly in xaml.cs
files.
It is recommended that you write code using the MVVM pattern
ViewModel
public class podgladUzytkownika : INotifyPropertyChanged
{
private string imie;
private string nazwisko;
private string mail;
public string Mail
{
get => mail;
set => this.SetValue(ref mail, value);
}
public string Nazwisko
{
get => nazwisko;
set => this.SetValue(ref nazwisko, value);
}
public string Imie
{
get => imie;
set => this.SetValue(ref imie, value);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetValue<T>(ref T oldValue, T newValue, [CallerMemberName] string propertyName = null)
{
oldValue = newValue;
OnPropertyChanged(propertyName);
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MainWindowViewModel
{
private int refreshCount;
public ICommand RefreshCommand { get; }
public ICommand PodgladUzytkownikaShow { get; }
public podgladUzytkownika data { get; }
public MainWindowViewModel()
{
data = new podgladUzytkownika();
PodgladUzytkownikaShow = new Command(PodgladUzytkownikaShowExecute);
RefreshCommand = new Command(RefreshCommandExecute);
}
private void PodgladUzytkownikaShowExecute(object obj)
{
var window = new Window();
window.DataContext = data;
window.Show();
}
private void RefreshCommandExecute(object obj)
{
// Data updates are passed to the view
refreshCount++;
data.Imie = nameof(data.Imie) + refreshCount;
data.Nazwisko = nameof(data.Nazwisko) + refreshCount;
data.Mail = nameof(data.Mail) + refreshCount;
}
}
View
// MainWindow.xaml
<StackPanel x:Name="StackPanel1">
<Button Content="PodgladUzytkownika" Command="{Binding Path=PodgladUzytkownikaShow}"/>
<Button Content="Refresh" Command="{Binding Path=RefreshCommand}"/>
</StackPanel>
// window.xaml
<StackPanel >
<TextBlock Text="{Binding Path=Imie }"/>
<TextBlock Text="{Binding Path=Nazwisko }"/>
<TextBlock Text="{Binding Path=Mail }"/>
</StackPanel>
Demo
this.MainWindow.DataContext =new MainWindowViewModel();
After chasing one problem after another with your comments, the problem is your code is not well-designed. Using data bindings (one of the prime benefits of WPF), you can stop chasing your tail with trying to figure out how to update one UI when data changes. Here is a simplified version of your code that will always ensure the UI matches the data you wish to manipulate.
MainWindow.xaml
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Label Margin="3" Grid.ColumnSpan="3">Lista użytkowników</Label>
<Button Margin="3" Padding="3" Grid.Row="2" Grid.ColumnSpan="3" Click="Zamknij_Click">Zamknij</Button>
<StackPanel Margin="3" Grid.Column="2" Grid.Row="1">
<!--<Button Name="Dodaj" Click="Dodaj_Click" Margin="3" Padding="10,3" >Dodaj...</Button>-->
<!--<Button Name="Usun" IsEnabled="False" Click="Usun_Click" Margin="3" Padding="10,3" >Usuń</Button>-->
<!--<Button Name="Edytuj" IsEnabled="False" Click="Edytuj_Click" Margin="3" Padding="10,3" >Edytuj...</Button>-->
<Button Name="Podglad" IsEnabled="False" Click="Podglad_Click" Margin="3" Padding="10,3" >Podgląd...</Button>
</StackPanel>
<ListView SelectionMode="Single" SelectionChanged="Selection_Changed" Name="lv_uzytkownicy" Margin="3" Grid.Row="1">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Imię"
DisplayMemberBinding="{Binding Imie}"/>
<GridViewColumn Header="Nazwisko"
DisplayMemberBinding="{Binding Nazwisko}" />
<GridViewColumn Header="Mail"
DisplayMemberBinding="{Binding Mail}"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<GridSplitter Grid.Column="1" Grid.Row="1" Width="5" ResizeDirection="Columns" HorizontalAlignment="Center"/>
</Grid>
MainWindow.xaml.cs
using System.Collections.ObjectModel;
public partial class MainWindow : Window
{
public ObservableCollection<Uzytkownik> listaUzytkownikow = new ObservableCollection<Uzytkownik>();
Podglad_Uzytkownika podgladUzytkownika;
public MainWindow()
{
InitializeComponent();
lv_uzytkownicy.ItemsSource = listaUzytkownikow;
listaUzytkownikow.Add(new Uzytkownik("Mietek", "Żul", "sikalafa#wp.pl"));
listaUzytkownikow.Add(new Uzytkownik("Franek", "Alpinista", "halo#gmail.pl"));
listaUzytkownikow.Add(new Uzytkownik("Stefan", "Ulążka", "mam.to#o2.pl"));
this.DataContext = this;
}
private void Podglad_Click(object sender, RoutedEventArgs e)
{
podgladUzytkownika = new Podglad_Uzytkownika();
podgladUzytkownika.DataContext = lv_uzytkownicy.SelectedItem;
podgladUzytkownika.Show();
}
private void Zamknij_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void Selection_Changed(object sender, SelectionChangedEventArgs e)
{
if (lv_uzytkownicy.SelectedItems.Count > 0) Podglad.IsEnabled = true;
else Podglad.IsEnabled = false;
if (podgladUzytkownika != null && podgladUzytkownika.IsVisible)
{
podgladUzytkownika.DataContext = lv_uzytkownicy.SelectedItem;
}
}
}
Podglad_Uzytkownika.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Margin="3">Imię</Label>
<Label Margin="3" Grid.Row="1">Nazwisko</Label>
<Label Margin="3" Grid.Row="2">Email</Label>
<TextBox Name="imieTextBox" Text="{Binding Imie, UpdateSourceTrigger=PropertyChanged}" Margin="3" Grid.Column="1"/>
<TextBox Name="nazwiskoTextBox" Text="{Binding Nazwisko, UpdateSourceTrigger=PropertyChanged}" Margin="3" Grid.Column="1" Grid.Row="1"/>
<TextBox Name="mailTextBox" Text="{Binding Mail, UpdateSourceTrigger=PropertyChanged}" Margin="3" Grid.Column="1" Grid.Row="2"/>
<Grid HorizontalAlignment="Center" Grid.ColumnSpan="2" Grid.Row="3" Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="pierwsza" />
</Grid.ColumnDefinitions>
<Button Margin="3" Padding="20, 5" Name="Podglad" Click="Podglad_Click" IsDefault="True">Zamknij</Button>
</Grid>
</Grid>
Podglad_Uzytkownika.xaml.cs
public partial class Podglad_Uzytkownika : Window
{
public Podglad_Uzytkownika()
{
InitializeComponent();
}
private void Podglad_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
Uzytkownik.cs
public class Uzytkownik : INotifyPropertyChanged
{
private string imie;
private string nazwisko;
private string mail;
public Uzytkownik(string imie, string nazwisko, string mail)
{
this.Imie = imie;
this.Nazwisko = nazwisko;
this.Mail = mail;
}
public string Imie { get => this.imie; set { this.imie = value; OnPropertyChanged(); } }
public string Nazwisko { get => this.nazwisko; set { this.nazwisko = value; OnPropertyChanged(); } }
public string Mail { get => this.mail; set { this.mail = value; OnPropertyChanged(); } }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
i am trying to bind the background color with the item template containing a grid in it. I want that depending upon the status the background color of each item in a list should be highlighted automatically. So i am doing it but no color is shown on the respective item.so how to bind the grid background such that color is changed. Xaml code :
<phone:LongListSelector x:Name="ResultListBox"
Margin="35,10,35,-25"
ItemsSource="{Binding Country}"
ItemTemplate="{StaticResource CustomList}"
Height="436" SelectionChanged="ResultListBox_SelectionChanged" Loaded="Listbox_loaded">
<UserControl.Resources>
<DataTemplate x:Key="CustomList">
<Grid Margin="5,0,10,5" Tag="{Binding Name}" x:Name="CountryGrid" Tap="BorderColor" >
<Grid.Background>
<SolidColorBrush Color="{Binding HighlightBackgroundColor}" />
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="450"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="45"></RowDefinition>
</Grid.RowDefinitions>
<Border VerticalAlignment="Center" x:Name="HighlightBG" HorizontalAlignment="Left" Grid.Column="0" Margin="0,5,0,0" Height="70" CornerRadius="0,10,10,0" Grid.Row="0" >
<StackPanel Orientation="Vertical" Margin="0,5,0,0" HorizontalAlignment="Center" >
<TextBlock Text="{Binding Name}" x:Name="nametextblock" VerticalAlignment="Top" TextWrapping="Wrap" Foreground="White" FontSize="30" HorizontalAlignment="Center" ></TextBlock>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
</UserControl.Resources>
c# code snippet:
public partial class ListPopup : UserControl,INotifyPropertyChanged
{
public ListPopup()
{
InitializeComponent();
this.Loaded += ListPopup_Loaded;
this.IsSelected = false;
//, INotifyPropertyChanged
this.NonHighlightColor = new SolidColorBrush(Colors.Transparent);
this.HighLightColor = new SolidColorBrush(Colors.Red);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
////////////
private bool _is_selected;
public bool IsSelected
{
get { return _is_selected; }
set
{
_is_selected = value;
OnPropertyChanged("HighlightBackgroundColor");
}
}
public SolidColorBrush HighlightBackgroundColor
{
get { if (IsSelected) return HighLightColor; else return NonHighlightColor; }
}
private SolidColorBrush HighLightColor { get; set; }
private SolidColorBrush NonHighlightColor { get; set; }
}
You're exposing a Brush but binding it as a Color. Just change your XAML:
<Grid Margin="5,0,10,5" Background="{Binding HighlightBackgroundColor}" Tag="{Binding Name}" x:Name="CountryGrid" Tap="BorderColor" >
What I did is a search function to search for events.
My problem here is that there's a property that fires up when i enter the page and set my ObservableCollection count to 0, but there are events details in my database.
How this should have work is that when i enter on the page there is a relay command that executes and retrieve all data from database and place it into the observable collection and display the event names into a ListBox. It I only when I enter a character then after it detect the events.
Here are my codes:
xaml:
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Loaded">
<Core:InvokeCommandAction Command="{Binding Searchfromhomepage.EventSearch, Mode=OneWay}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<Grid x:Name="LayoutRoot">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--<TextBox x:Name="txtSearch" Background="White" Text="{Binding Path=HomePage.TxtEntered, UpdateSourceTrigger=PropertyChanged}" FontSize="30" Height="57" Margin="19,10,19,0" Grid.Row="1" />-->
<TextBox x:Name="txtTest2" Text="{Binding Searchfromhomepage.Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.667,19,0">
<ListBox Background="Black" x:Name="listBox" FontSize="26" Margin="0,10,0,0" ItemsSource="{Binding Searchfromhomepage.FilteredNames, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="txtEventName" TextWrapping="Wrap" Text="{Binding EventName}" Tapped="txtEventName_Tapped" IsTapEnabled="True" Foreground="White" Width="300" Margin="10,15,0,0" Height="55"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
viewmodel codes:
private static ObservableCollection<Event> _searchEventCollection = new ObservableCollection<Event>();
public static ObservableCollection<Event> SearchEventCollection
{
get { return _searchEventCollection; }
set { _searchEventCollection = value; }
}
//search from homepage event section
private RelayCommand _eventSearch;
/// <summary>
/// Gets the EventSearch.
/// </summary>
public RelayCommand EventSearch
{
get
{ return _eventSearch
?? (_eventSearch = new RelayCommand(
async () =>
{
SearchEventCollection.Clear();
var eventList = await App.MobileService.GetTable<Event>().ToListAsync();
foreach (Event ename in eventList)
{
SearchEventCollection.Add(new Event
{
Id = ename.Id,
EventName = ename.EventName,
Date = ename.Date,
Location = ename.Location,
Desc = ename.Desc
});
}
}));
}
}
private string filter;
public String Filter
{
get
{
return this.filter;
}
set
{
this.filter = value;
RaisePropertyChanged("FilteredNames");
}
}
public List<Event> FilteredNames
{
get
{
if (filter == "")
{
return SearchEventCollection.ToList();
}
else
{
return (from name in SearchEventCollection where name.EventName.ToUpper().StartsWith(filter.ToUpper()) select name).ToList();
}
}
}
public searchfromhomepageViewModel()
{
filter = "";
}
in your view model code set up the _eventSearch command like so
public RelayCommand _eventSearch = new RelayCommand(EventSearch);
I have the following requirements:
Window will show a ListView with multiple items.
User should be able to check (Checkbox) any item.
a) If one item, all items should be unchecked and disabled.
b) If checked item is unchecked, than all items should be enabled.
As of now, I have the following incomplete code.
MainWindow XAML:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="520.149" Width="732.463">
<Window.Resources>
<ResourceDictionary Source="MainWindowResource.xaml" />
</Window.Resources>
<Grid>
<ListView x:Name="myListBox" ItemTemplate="{StaticResource OfferingTemplate}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
</Window>
DataTemplete for ListView:
<DataTemplate x:Key="OfferingTemplate">
<StackPanel>
<Grid IsEnabled="{Binding IsEnabled}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8"></ColumnDefinition>
<ColumnDefinition Width="120"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Rectangle Grid.Column="0" Grid.RowSpan="3" Fill="#F4CA16" />
<Label
Grid.Column="1"
Grid.Row="0"
Content="{Binding Title}"
FontSize="18" FontWeight="Bold"
Margin="0,0,0,0" />
<TextBlock
Grid.Column="1"
Grid.Row="1"
FontSize="10"
Text="{Binding Description}"
Foreground="Black"
TextWrapping="WrapWithOverflow"
Margin="5,0,0,0" />
<CheckBox
Grid.Column="1"
Grid.Row="2"
FontSize="14"
IsChecked="{Binding IsSelected}"
VerticalAlignment="Bottom"
Margin="5,0,0,0">
<TextBlock Text="Select" Margin="0,-2,0,0"/>
</CheckBox>
</Grid>
</StackPanel>
</DataTemplate>
Model:
class MyModel
{
public string Title { get; set; }
public string Description { get; set; }
public bool IsSelected { get; set; }
public bool IsEnabled { get; set; }
}
ViewModel:
class MyViewModel : INotifyPropertyChanged
{
private MyModel offering;
public MyViewModel()
{
offering = new MyModel();
}
public int ID { get; set; }
public string Title
{
get { return offering.Title; }
set
{
offering.Title = value;
RaisePropertyChanged("Title");
}
}
public string Description
{
get { return offering.Description; }
set
{
offering.Description = value;
RaisePropertyChanged("Description");
}
}
public bool IsSelected
{
get { return offering.IsSelected; }
set
{
offering.IsSelected = value;
RaisePropertyChanged("IsSelected");
}
}
public bool IsEnabled
{
get { return offering.IsEnabled; }
set
{
offering.IsEnabled = value;
RaisePropertyChanged("IsEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This is an interesting question. Since the action you want applies to all items in the list, this logic should in list class level. Your MyViewModel class is fine. You need add some logic in your list class and XAML but thanks to Prism, it is quite easy.
The list class (not shown in your post) Contains:
public ObservableCollection<MyViewModel> MyItems { get; set; } //Binding to ItemsSource
private ICommand _selectCommand;
public ICommand SelectCommand
{
get { return _selectCommand ?? (_selectCommand = new DelegateCommand<MyViewModel>(DoSelect)); }
}
private void DoSelect(MyViewModel myViewModel)
{
foreach(var item in MyItems)
if (item != myViewModel)
{
item.IsSelected = false;
item.IsEnabled = false;
}
}
private ICommand _unselectCommand;
public ICommand UnselectCommand
{
get { return _unselectCommand ?? (_unselectCommand = new DelegateCommand<MyViewModel>(DoUnselect)); }
}
private void DoUnselect(MyViewModel myViewModel)
{
foreach (var item in MyItems)
if (item != myViewModel)
{
item.IsEnabled = true;
}
}
There are two commands, one for selecting and the other for unselecting. The magic is on XAML:
<ListView ItemsSource="{Binding Path=MyItems}" x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected}" IsEnabled="{Binding Path=IsEnabled}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.SelectCommand}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.UnselectCommand}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Using Prism's triggers, you can map CheckBox's Checked and Unchecked event to your list view model's commands and passing the item view model as parameter.
It is working perfectly but one thing is annoying, that setting item's IsSelected is separate. When you check a CheckBox, the item behind is set to true through DataBinding but all others are set through parent view model. If your post is all your requirement, you can remove IsChecked binding and put the logic of setting one IsSelected inside list view model, which looks clenaer and easier to write test code.