Data not displaying in styled WPF ListView - c#

I'm trying to create an application at work to display jobs assigned to our team members and this is the first time I've worked with WPF. I did a simple test project before this to make sure I understood how things work in WPF compared to WindowForms and I am stuck on getting my data to display in my listviews. During the test project I was able to set and display the data fine, but once I moved to the live project and started making styling and really segregating the code for readability and maintainability my issue arises. Here is the XAML code for the list view style:
<Style x:Key="baseListViewstyle" TargetType="ListView">
<Setter Property="FontFamily" Value="Gautami"/>
<Setter Property="FontSize" Value="10"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListView">
<ListView>
<ListView.View>
<GridView>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="PRF" DisplayMemberBinding="{Binding Path=[0]}"/>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="Client ID" DisplayMemberBinding="{Binding Path=[1]}"/>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="Drop Date" DisplayMemberBinding="{Binding Path=[2]}"/>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="QTY" DisplayMemberBinding="{Binding Path=[3]}"/>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="Type" DisplayMemberBinding="{Binding Path=[4]}"/>
</GridView>
</ListView.View>
</ListView>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I'm 100% certain the issue is from this style, but I'm not sure how to resolve it. I've taken out the style and the ListViews will display all the items being added into them from the code behind, but there are no headers (obviously) and it merely displays the type of the item added in (in this case the ListViews state String[] x number of times). I would rather not move the header declarations into the window's XAML but keep it segregated in my style.xaml, as these ListViews are going to be reused a dozen times or so throughout the code, and most of which will be added in dynamically, so pointing to unified style will make maintenance exponentially easier. I feel like this is a simple issue in the style, maybe I need a different binding or should the headers be declared differently since it is happening in a style? For reference here is the code behind where I am adding the data to the list view.
private static void populateListView(ListView lv, List<Label> labels)
{
if (lv.Tag != null)
{
SqlConnectionStringBuilder conString = new SqlConnectionStringBuilder();
conString.IntegratedSecurity = true;
conString.DataSource = "oh50ms04\\Server_1";
using (SqlConnection con = new SqlConnection(conString.ToString()))
{
con.Open();
string sqlCMD = getSQLCommand(lv.Tag.ToString(), labels);
SqlCommand cmd = new SqlCommand(sqlCMD, con);
try
{
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
string[] arr = new string[] { reader["JobID"].ToString(), reader["ClientID"].ToString(), DateTime.Parse(reader["RDD"].ToString()).ToString("MM/dd/yy"), reader["QuantityFinishedPieces"].ToString(), reader["Type"].ToString() };
lv.Items.Add(arr);
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
}
}

The ControlTemplate defines the (entire) appearance of a control so you are effectively "overwriting" the appearance of the ListView that you are populating with the empty one in your Style.
Try to set the View property of the ListView to a GridView in the Style instead of overriding the template:
<Style x:Key="baseListViewstyle" TargetType="ListView" x:Shared="False">
<Setter Property="FontFamily" Value="Gautami"/>
<Setter Property="FontSize" Value="10"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="View">
<Setter.Value>
<GridView>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="PRF" DisplayMemberBinding="{Binding Path=[0]}"/>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="Client ID" DisplayMemberBinding="{Binding Path=[1]}"/>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="Drop Date" DisplayMemberBinding="{Binding Path=[2]}"/>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="QTY" DisplayMemberBinding="{Binding Path=[3]}"/>
<GridViewColumn Width="{StaticResource ResourceKey=columnWidth}" Header="Type" DisplayMemberBinding="{Binding Path=[4]}"/>
</GridView>
</Setter.Value>
</Setter>
</Style>

Related

How to detect which row and column clicked in listview in C# WPF

I have one wpf project and in that I have one form and it it I have added one listview. I have loaded total 8 rows in it. And I have additionally added 2 more columns with text as Edit and Remove. Now what I need is on click of Edit and Remove click the respective row column details will come. Like if user click on Edit than the edit code should executes. Same thing with the Remove click also. I am attaching my wpf xaml file code here
<ListView x:Name="lstViewIrctcId" HorizontalAlignment="Left" Height="271" Margin="4,140,0,0" VerticalAlignment="Top" Width="777" >
<ListView.Resources>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
<EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn x:Name="Id" Header="ID" Width="135" DisplayMemberBinding="{Binding ID}" />
<GridViewColumn x:Name="Count" Header="TOTAL COUNT" Width="80" DisplayMemberBinding="{Binding COUNT}" />
<GridViewColumn x:Name="Name" Header="NAME" Width="80" DisplayMemberBinding="{Binding NAME}" />
<GridViewColumn x:Name="Address" Header="Address" Width="80" DisplayMemberBinding="{Binding ADDRESS}" />
<GridViewColumn x:Name="Status" Header="Status" Width="80" DisplayMemberBinding="{Binding STATUS}" />
<GridViewColumn x:Name="irEdit" Header="Edit" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="Edit" MouseLeftButtonUp="Edit_btn_click">
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="irRemove" Header="Remove" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="Edit" MouseLeftButtonUp="Delete_btn_click">
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I have tried adding the MouseLeftButtonUp in edit and remove and created its methods in .cs file. This detects which column clicked ie. Edit or Remove but I also need the respective row details in order to process the Edit and Remove operations.
in your list view xaml you should pass the listview items in a command like :
Command="{Binding GetSelectedItem}" CommandParameter="{Binding Path=PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
and in your code, after declaring the command as Relay command or delegateCommand you can call this function
public void YourCommandMethod(object obj)
{
try
{
//Get our list view
ListView listview = (ListView)obj;
string sNavigationPath = string.Empty;
//this is a work around to get selected item without clicking on list view, just the coordinates
var item = VisualTreeHelper.HitTest(listview, Mouse.GetPosition(listview)).VisualHit;
// find ListViewItem (or null)
while (item != null && !(item is ListViewItem))
item = VisualTreeHelper.GetParent(item);
if (item != null)
{
//Convert item to Listview
ListViewItem listviewItem = (ListViewItem)item;
//Get the data context wich hold the necessary info
var _dataContext = listviewItem.DataContext;
//Convert back to our customized listview
var _list = (ListViewItems)_dataContext;
// Do whatever you want with your elements
}
}
catch (Exception ex)
{
StaticElements.showErrorFlyout(ex.Message);
}
}

ListView ItemsSource binding

I have a ListView with 3 columns:
<ListView ItemsSource="{Binding ParamName}" HorizontalAlignment="Left" Height="109" Margin="10,87,0,0" VerticalAlignment="Top" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Width="281">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn Header="Name" Width="60"/>
<GridViewColumn Header="Type" Width="60"/>
<GridViewColumn Header="Content" Width="156"/>
</GridView>
</ListView.View>
</ListView>
Currently, I have set ParamName as my ItemsSource, which will obviously not achieve the desired result this way.
In my ViewModel, I have ObservableCollections for each Column (ParamName for Name, ParamType for Type and ParamContent for Content). Those ObservableCollections are correctly filled and I am able to receive their data through the Binding, but I cannot fill the columns with their respective data.
I have thought of certain possible solutions, but none of them seem to work. What would be the best approach for this problem?
Here's how it looks like (left) and how it should look like (right):
Naming them after their Types might be a little bit confusing.
You have three Collections, but only one is binded. So all columns have same value.
Try to create one Collection containing all you need:
ObservableCollection<MyRow>
where MyRow is a Class or Struct with Properties you need.
If you already have these Collections - try to concatenate it to one major Collection and tell the GridColumns which Properties you wish to bind to each Column, but I'm not sure if it's possible with ObservableCollections - what if they have different length?
And you can still create your own Collection from these three - just parse it...

how i access textbox inside a listview Column

i have a list view like this .
<ListView x:Name="Source_List"
ItemsSource="{Binding Lines}"
IsSynchronizedWithCurrentItem="True"
SelectionChanged="Source_List_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Line"
Width="50"
DisplayMemberBinding="{Binding LineNumber}" />
<GridViewColumn Header="Start Time"
Width="100"
DisplayMemberBinding="{Binding StartTime , Converter={StaticResource LineTimeToString}}" />
<GridViewColumn Header="End Time"
Width="100"
DisplayMemberBinding="{Binding EndTime ,Converter={StaticResource LineTimeToString}}" />
<GridViewColumn Header="Text"
Width="500">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Context ,Mode=TwoWay}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Original Text"
DisplayMemberBinding="{Binding Context ,Mode=OneTime}" />
</GridView>
</ListView.View>
</ListView>
I want to access the text box inside the selected items as a textbox.(in code behind )
how can i do this?
i used this article by beth massi.
you can acces to textbox ui element (if your texblock is clicked)
TextBlock content = ((FrameworkElement)e.OriginalSource) as TextBlock;
else if your texblock is inside a grid use
Grid c = ((FrameworkElement)e.OriginalSource) as Grid;
and search for the grid(c) children.
Because you are using IsSynchronizedWithCurrentItem="True" you should be able to access the current item of your list 'Lines'. Your text box is bound to Context so I would assume in your view model you would be able to call 'Lines.CurrentItem.Context'

Change the foreground color of a ListView item only in one column

I have a List of objects and a ListView where I display this list. Such an object has some properties, they are bound to the columns of the ListView.
<ListView x:Name="_fileNameList" FontSize="12" SourceUpdated="_fileNameList_SourceUpdated" TargetUpdated="_fileNameList_TargetUpdated">
<ListView.View>
<GridView x:Name="FileNameAttributes" >
<GridViewColumn Header="File Name" Width="200" DisplayMemberBinding="{Binding fileName}"/>
<GridViewColumn Header="Size" Width="80" DisplayMemberBinding="{Binding size}"/>
<GridViewColumn Header="Date" Width="80" DisplayMemberBinding="{Binding date}"/>
<GridViewColumn Header="Time" Width="80" DisplayMemberBinding="{Binding time}"/>
<GridViewColumn Header="New Name" Width="300" DisplayMemberBinding="{Binding newFileName}"/>
</GridView>
</ListView.View>
</ListView>
This part is working fine.
Now I want to change the Foreground color of the newFileName column in a single row, but only if it is equal to the 'fileName' in the same row.
Can I do this in XAML or do I have to go to the code behind file?
I would like it best if I could handle it in XAML, because I think its a pure design issue, but I have no idea, where to start or where to put this, (can I do String comparisons in XAML?)
So I tried to handle this in the code behind file. I thought there must be an event that is raised when the ListView has changed, I tried the SourceUpdated event, but it is not entered when I change the content of my list.
The next problem would be, how to access those ListView items ...
Can anyone give me an idea how I can solve this?
You can do this using MultiBinding and MultiConverter.
You will need to write a MultiConverter which takes fileName and newFileName and returns true if they are equal
<ListView x:Name="_fileNameList" FontSize="12" SourceUpdated="_fileNameList_SourceUpdated" TargetUpdated="_fileNameList_TargetUpdated">
<ListView.View>
<GridView x:Name="FileNameAttributes" >
<GridViewColumn Header="File Name" Width="200" DisplayMemberBinding="{Binding fileName}"/>
<GridViewColumn Header="Size" Width="80" DisplayMemberBinding="{Binding size}"/>
<GridViewColumn Header="Date" Width="80" DisplayMemberBinding="{Binding date}"/>
<GridViewColumn Header="Time" Width="80" DisplayMemberBinding="{Binding time}"/>
<GridViewColumn Header="New Name" Width="300">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding newFileName}">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Foreground" Value="Black"></Setter>
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource EqualityConverter}">
<Binding Path="newFileName"></Binding>
<Binding Path="fileName"></Binding>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="TextBlock.Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The code for EqualityConverter is as below :
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0].ToString().Equals(values[1].ToString()))
return true;
return false;
}
if you wanna change the style of any wpf element you should use the Style. if you want it happen on some conditions you should use Style.Triggers. if your conditions belong to binding information you should use DataTrigger. And if the XAML datatrigger can not handle your condition add a converter.
so i would suggest you to take a datatrigger in your GridViewColum.Style and a multiconverter with 2 parameters filename and newfilename. return true if it the same otherwise false.
something like that:
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MyFileNameCheckConverter}">
<Binding Path="fileName"/>
<Binding Path="newFileName"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
converter
public class FileNameCheckConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var filename = (string)values[0];
var newfilename = (string)values[1];
return filename==newfilename;
}
...
}
ps: dont forget to set the "normal" foreground in your style too! otherwise the trigger stuff will not work.
pps: code is handwritten

C#/WPF: Get Selected Row from a ListView

I've following ListView Item (in a WPF Form):
<ListView Name="listViewTeam" ItemsSource="{Binding Path=TeamList}">
<ListView.View>
<GridView ColumnHeaderTemplate ="{StaticResource BlueHeader}">
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn Width="34" Header="Nr" DisplayMemberBinding="{Binding Path=TeamNr , Mode=OneWay}"/>
<GridViewColumn Header="Team" DisplayMemberBinding="{Binding Path=TeamName, Mode=OneWay}"/>
</GridView>
</ListView.View>
</ListView>
DataContext is a TeamViewModel, which contains
a) a List of Teams
b) a SelectedTeam Property (which is a Team-Object and contains Team.TeamName and Team.TeamNr )
Loading of the TeamViewModel.TeamListe into the ListView works fine (I get all Team-Objects from the list displayed in my ListView)
How can I set the TeamViewModel.SelectedTeam Property to the Row-Value, which is selected?
Thanks!
Cheers
Set the ListView's SelectedItem property to {Binding Path=SelectedTeam}

Categories