Updatinging an XAML GridView from C# in .NET 3.5 - c#

I have a simple (I think) app that reads an SQL database into variables in my program, and I then need to update a gridview defined in XAML with the data I read. I'm constrained to code for .NET 3.5. All the searching I've done on the books I have on XAML, MS .NET help and elsewhere on the Web shows countless examples of doing this from ASP.NET, but none from a C#-XAML combination. I've found doing simple things in XAML ++much++ more difficult and involved than doing the same thing in Winforms, and I'm just not getting this. In particular, data binding seems to me to be a black art. Can someone please look at my XAML and tell me what I need to do or change to populate this control from my C# code-behind?
Here is my XAML:
<Window x:Class="UCCResourceManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UCC Resource Mangler" Height="350" Width="700">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="250"/>
<RowDefinition />
</Grid.RowDefinitions>
<ListView Name="grdResource"
ItemsSource="{Binding}"
Grid.Row="0">
<ListView.View>
<GridView AllowsColumnReorder="false"
ColumnHeaderToolTip="UCC Resource Table">
<GridViewColumn DisplayMemberBinding="{Binding Path=ID}"
Header="ID"
Width="50"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=LocationID}"
Header="LocationID"
Width="75"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Type}"
Header="Type"
Width="50"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Name}"
Header="Name"
Width="200"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Enabled}"
Header="Enabled"
Width="50"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Flags}"
Header="Flags"
Width="50"/>
</GridView>
</ListView.View>
</ListView>
<Button Name="btnOK"
Content="OK"
Grid.Row="1"
Width="100"
Height="20
" Click="btnOK_Click" />
</Grid>
</Window>

I recommand to use MVVM. There you have a ViewModel class where you can have your properties to bind to.
public class MainWindowViewModel
{
#region Constructor
public MainWindowViewModel()
{
YourGridList = new ObservableCollection<GridElement>();
var el = new GridElement
{
Element1 = "element 1",
Element2 = "element 2",
Element3 = "element 3"
};
YourGridList.Add(el);
}
#endregion
#region Private members
private ObservableCollection<GridElement> _yourGridList;
private ICommand _addElementCommand;
#endregion
#region Public properties
public ObservableCollection<GridElement> YourGridList
{
get
{
return _yourGridList;
}
set
{
_yourGridList = value;
}
}
#endregion
#region Commands
public ICommand AddElementCommand
{
get { return _addElementCommand ?? (_addElementCommand = new DelegateCommand(AddElement)); }
}
#endregion
#region Private Methods
private void AddElement()
{
var el = new GridElement
{
Element1 = "NewEl1",
Element2 = "NewEl2",
Element3 = "NewEl3"
};
YourGridList.Add(el);
}
#endregion
Then, you can use in the xaml.cs file this class as DataContext.
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
And from the xaml file you can have your ListView ItemsSource populated from the "YourGridList" property from the ViewModel.
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Content="Add element" Command="{Binding AddElementCommand}" Grid.Row="0"/>
<ListView Grid.Row="1"
Margin="10"
ItemsSource="{Binding YourGridList, UpdateSourceTrigger=PropertyChanged}"
MaxHeight="300">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Width="Auto">
<GridViewColumn.Header>
<GridViewColumnHeader Content="Element 1" HorizontalContentAlignment="Left" />
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Element1}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="Auto">
<GridViewColumn.Header>
<GridViewColumnHeader Content="Element 2" HorizontalContentAlignment="Left" />
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Element2}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="Auto" >
<GridViewColumn.Header>
<GridViewColumnHeader Content="Element 3" HorizontalContentAlignment="Left" />
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Element3}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Grid>
This should work for updating your ListView and have a clear decoupling between the code and the xaml part.

Related

WPF ListView not caching images

I have a WPF application that uses a ListView with a grid that displays images directly from the web. When the list is populated the images load as expected, but as I scroll down (the list contains around 200 items on average) it starts reusing the items that aren't in view (as it should). However, this causes the images to be released from memory and as a result they get reloaded all over again when the user scrolls back up.
MainWindow.xaml
<ListView Grid.Row="3" ItemsSource="{Binding SearchResults}" Background="{StaticResource PrimaryBackground}" Foreground="{StaticResource PrimaryForeground}"
ui:GridViewSort.AutoSort="True" ui:GridViewSort.ShowSortGlyph="False" IsSynchronizedWithCurrentItem="True" VirtualizingStackPanel.IsVirtualizing="False">
<ListView.View>
<GridView>
<GridViewColumn Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="{x:Type Foo}">
<Image>
<Image.Source>
<BitmapImage CacheOption="OnDemand" UriSource="{Binding PreviewImageUrl}" />
</Image.Source>
</Image>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title}" ui:GridViewSort.PropertyName="Title" />
<GridViewColumn Header="Remix" DisplayMemberBinding="{Binding Remix}" ui:GridViewSort.PropertyName="Remix" />
<GridViewColumn Header="Artist" DisplayMemberBinding="{Binding Artist}" ui:GridViewSort.PropertyName="Artist" />
<GridViewColumn Header="Duration" DisplayMemberBinding="{Binding Duration}" ui:GridViewSort.PropertyName="Duration" />
<GridViewColumn Header="BPM" DisplayMemberBinding="{Binding Bpm}" ui:GridViewSort.PropertyName="Bpm" />
<GridViewColumn Header="Year" DisplayMemberBinding="{Binding Date}" ui:GridViewSort.PropertyName="Date" />
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="0,0,10,0" />
</Style>
</StackPanel.Resources>
<Button Command="{Binding ElementName=Window, Path=DataContext.Download}" CommandParameter="{Binding}">Download</Button>
<Button Command="{Binding ElementName=Window, Path=DataContext.CopyLink}" CommandParameter="{Binding}">Copy link</Button>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Setting the Image.Source property with a different CacheOption makes no difference. You can also see I disabled virtualization which is bad but it's the only way to have it keep the images in memory. Is there an easy way to stop this from happening while also enabling virtualization?
Add a readonly PreviewImage property to your search results item class that creates and holds the BitmapImage when it is first accessed:
public class SearchResult : INotifyPropertyChanged
{
private Uri previewImageUrl;
public Uri PreviewImageUrl
{
get { return previewImageUrl; }
set
{
previewImageUrl = value;
previewImage = null;
NotifyPropertyChanged(nameof(PreviewImageUrl));
NotifyPropertyChanged(nameof(PreviewImage));
}
}
private ImageSource previewImage;
public ImageSource PreviewImage
{
get
{
if (previewImage == null && previewImageUrl != null)
{
previewImage = new BitmapImage(previewImageUrl);
}
return previewImage;
}
}
...
}
and bind to it like this:
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding PreviewImage}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
Try this:
<Image
HorizontalOptions="CenterAndExpand"
VerticalOptions ="CenterAndExpand">
<Image.Source>
<UriImageSource Uri="{Binding Image}"
CacheValidity="14"
CachingEnabled="true"/>
</Image.Source>
</Image>

How To bind ListView values depends on combobox selected item?

Hi all i want to bind my listbox depends on combobox selected item here is my code
<StackPanel Orientation="Horizontal" >
<ComboBox Name="cmbID" Width="150" Margin="10" Height="30" SelectedItem="{Binding CmbSelected,Mode=TwoWay}" DisplayMemberPath="ID" ItemsSource="{Binding MyStudent,Mode=TwoWay}"/>
<Button Name="btnGetDetail" Margin="10" Command="{Binding getDetails}" Content="Get Details" Height="30" Width="90"/>
<TextBox Name="tbName1" Width="90" Height="30" Text="{Binding ElementName=cmbID,Path= SelectedItem.Sub}"></TextBox>
</StackPanel>
In Above code i am binding my combobox to one observable collection and want to bind my ListView to selected item of combobox below is my code
<ListView Name="myStudent" ItemsSource="{Binding CmbSelected,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Width="420" Height="150">
<ListView.View >
<GridView x:Name="grdStudentDetails">
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ElementName=cmbID,Path=SelectedItem.ID}" Width="30"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding ElementName=cmbID,Path =SelectedItem.Name}" Width="100"/>
<GridViewColumn Header="RollNum" DisplayMemberBinding="{Binding ElementName=cmbID,Path=SelectedItem.RollNum}" Width="100"/>
<GridViewColumn Header="Subject" DisplayMemberBinding="{Binding ElementName=cmbID,Path=SelectedItem.Sub}" Width="100"/>
<GridViewColumn Header="PhNumber" DisplayMemberBinding="{Binding ElementName=cmbID,Path=SelectedItem.PhNum}" Width="100"/>
</GridView>
</ListView.View>
</ListView>
I am not able to find where I am doing mistake even I bind my text box with same binding it is working fine. please refer textbox just below my combobox's XAML.
my viewmodle.cs code is as follow
private student cmbSelcted;
public student CmbSelected
{
get { return cmbSelcted; }
set { cmbSelcted = value; OnPropertyChanged("CmbSelected"); }
}
public ObservableCollection<student> MyStudent
{
get { return myStudent; }
set { myStudent = value; OnPropertyChanged("MyStudent"); }
}
Can't see the ItemsSource of your ListView! Hope you've bind it to an collection. If not - try this approach.
<ListView Grid.Row="1" ItemsSource="{Binding SelectedStudents, Mode=OneWay}">
<ListView.View >
<GridView x:Name="grdStudentDetails">
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}" Width="30"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="100"/>
<GridViewColumn Header="RollNum" DisplayMemberBinding="{Binding RollNum}" Width="100"/>
<GridViewColumn Header="Subject" DisplayMemberBinding="{Binding Sub}" Width="100"/>
<GridViewColumn Header="PhNumber" DisplayMemberBinding="{Binding PhNum}" Width="100"/>
</GridView>
</ListView.View>
</ListView>
ViewModel
private Student _cmbSelected;
public Student CmbSelected
{
get { return _cmbSelected; }
set
{
_cmbSelected = value;
if (_cmbSelected != null)
{
SelectedStudents = new List<Student>() { _cmbSelected };
}
else
{
SelectedStudents = new List<Student>();
}
OnPropertyChanged();
}
}
private List<Student> _selectedStudents;
public List<Student> SelectedStudents
{
get { return _selectedStudents; }
set
{
_selectedStudents = value;
OnPropertyChanged();
}
}
Hope you see that it's important to have an ItemsSource on your ListView.
<Grid>
<StackPanel>
<ListView x:Name="usergrid" Margin="100,50,100,0" HorizontalAlignment="Center" FontSize="20" ItemsSource="{Binding Path=user1}">
<ListView.View>
<GridView>
<GridViewColumn Header="UserId" DisplayMemberBinding="{Binding UserId,Mode=TwoWay}" Width="100" ></GridViewColumn>
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName,Mode=TwoWay}" Width="150" />
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName,Mode=TwoWay}" Width="150" />
<GridViewColumn Header="City" DisplayMemberBinding="{Binding City,Mode=TwoWay}" Width="150" />
<GridViewColumn Header="State" DisplayMemberBinding="{Binding State,Mode=TwoWay}" Width="150" />
<GridViewColumn Header="Country" DisplayMemberBinding="{Binding Country,Mode=TwoWay}" Width="150" />
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Grid>

Listview in Usercontrol in Flyout remains empty

I'm trying to place a usercontrol in a flyout window. In this usercontrol I'm trying to bind a list to a listview, but the listview remains empty.
C# UserControl
public MusicList()
{
InitializeComponent();
}
public void Makemusiclist(Inhabitant inhabitant, Wait wait, Rectangle rect)
{
musicList.Width = rect.Width;
musicList.Height = rect.Height;
lViewMusicFiles.Width = rect.Width - 150; // place the box on the right plce on any screen
lViewMusicFiles.Height = rect.Height - 50;
List<MusicFile> playlist = new List<MusicFile>(); // get the inhabitants' playlist from his file
playlist = inhabitant.musicList;
lViewMusicFiles.DataContext = playlist;
lViewMusicFiles.ItemsSource = playlist;
lViewMusicFiles.Items.Refresh();
musicList.UpdateLayout();
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
{
wait.Close();
}));
}
XAML Usercontrol
<Grid>
<ListView x:Name="lViewMusicFiles" HorizontalAlignment="Left" VerticalAlignment="Top" Background="Red" Foreground="White" FontSize="32" Margin="150,50,0,0" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionMode="Single" SelectionChanged="lViewMusicFiles_SelectionChanged" Style="{DynamicResource ListViewStyle1}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Titel" Width="Auto" DisplayMemberBinding="{Binding Path=title}"/>
<GridViewColumn Header="Artist" Width="Auto" DisplayMemberBinding="{Binding Path=artist}"/>
<GridViewColumn Header="Album" Width="Auto" DisplayMemberBinding="{Binding Path=album}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</UserControl>
XAML flyout
<Grid>
<local:MusicList x:Name="musicList" />
</Grid>
Use ObservableCollection<T> instead of List<T>. you can write binding in XAML like this. I didn't write irrelevant properties.
<ListView x:Name="lViewMusicFiles" ItemsSource="{Binding Playlist, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<ListView.View>
<GridView>
<GridViewColumn Header="Titel" DisplayMemberBinding="{Binding title}"/>
<GridViewColumn Header="Artist" DisplayMemberBinding="{Binding artist}"/>
<GridViewColumn Header="Album" DisplayMemberBinding="{Binding album}"/>
</GridView>
</ListView.View>
</ListView>
Also you need this property.
public ObservableCollection<MusicFile> Playlist{ get; } = new ObservableCollection<MusicFile>();

Edit TextView column on ListView WPF

I'm Using WPF.
I have ListView with TextBox column and two checkboxs columns.
I want to edit the TextBox text by double click or something else.
What is the simple way to do that?
...
<GridViewColumn Header="Name" DisplayMemberBinding={Binding Path=fullName}" Width=500>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="txtName"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
Here is a sample way of doing this...
public partial class MainWindow : Window
{
public List<string> Items { get; set; }
public MainWindow()
{
InitializeComponent();
Items = new List<string>();
LoadItems();
DataContext = this;
}
private void txtName_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
TextBox currentTextBox = (TextBox)sender;
if (currentTextBox.IsReadOnly)
currentTextBox.IsReadOnly = false;
else
currentTextBox.IsReadOnly = true;
}
private void LoadItems()
{
Items.Add("Coffee");
Items.Add("Sugar");
Items.Add("Cream");
}
}
<Grid>
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="txtName" Text="{Binding Mode=OneTime}" IsReadOnly="True" MouseDoubleClick="txtName_MouseDoubleClick" Width="100"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
Here is an example I have from an application that I wrote. The column JobName was to be user editable. This example allows the column to be editable and also gets rid of the border and lets the background blend into the row so it doesn't appear to have a text box in it.
Those can be edited out (BorderThickness="0" Background="Transparent").
My example binds to an MVVM ViewModel property called JobName and is set to be "TwoWay" so that changes to the view model will also reflect on the UI.
<ListView x:Name="lvJobs" HorizontalAlignment="Left" Height="628" Margin="30,62,0,0" ItemsSource="{Binding Jobs}"
SelectedItem="{Binding SelectedJob, Mode=TwoWay}" VerticalAlignment="Top" Width="335">
<ListView.View>
<GridView>
<GridViewColumn Header="Active" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsActive, Mode=TwoWay}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Job Name" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding JobName, Mode=TwoWay}" BorderThickness="0" Background="Transparent"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding User}" Header="User" Width="125"/>
</GridView>
</ListView.View>
</ListView>

WPF - Filling a ListView

I have a 2 column ListView and I'm trying to fill it using and IDictionary with the following code.
System.Collections.IDictionary entryList = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
foreach (System.Collections.DictionaryEntry de in entryList)
{
Row row = new Row();
row.Name1 = (string)de.Key;
row.Name2 = (string)de.Value;
this.list1.Items.Add(row);
}
public class Row
{
public string Name1 { get; set; }
public string Name2 { get; set; }
}
XAML:
<ListView x:Name="varList"
Grid.ColumnSpan="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Height="400"
Width="500"
Margin="0, 30, 0, 0">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Name" />
<GridViewColumn Width="350" Header="Path" />
</GridView>
</ListView.View>
</ListView>
But every row and column gets filled with "Project.Views.Row".
Anyone got any idea on how to fix it? Thank you very much.
A ListView (and every other control for that matter) will display the results of calling ToString when given an object to display.
For a standard class, thats its qualified name; Project.Views.Row in your example.
There are two ways to fix this:
Don't add an object. Instead, format the string as you want it ie:
list1.Items.Add(String.Format({0}:{1}, row.Name1, row.Name2));
Do this the right way and use MVVM. In this case your XAML needs a data template:
<ListView ItemsSource="{Binding Rows}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name1}"/>
<TextBlock Text="{Binding Name2}"/>
</StackPanel>
</DataTemplate>
<ListView.ItemTemplate>
</ListView>
For a grid view:
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name1}" />
<GridViewColumn Header="Path" DisplayMemberBinding="{Binding Name2}"/>
</GridView>
</ListView.View>
Binding the ItemsSource is not actually necessary, but since we are doing everything the right way, you should do it so you are not directly manipulating the UI from code.

Categories