I am having a hard time binding a ListView with an ObservableCollection in another class.
My xaml:
<ListView Height="117" HorizontalAlignment="Left" Margin="20,239,0,0" Name="lvResults" VerticalAlignment="Top" Width="759" ItemsSource="{Binding RuleSearch.FileMatches}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding FileName}"/>
<GridViewColumn Header="Size" Width="120" DisplayMemberBinding="{Binding DirectoryName}"/>
<GridViewColumn Header="Date" Width="120" DisplayMemberBinding="{Binding Size}"/>
<GridViewColumn Header="Full Path" Width="120" />
<GridViewColumn Header="Some Meaningless Data" Width="120" />
</GridView>
</ListView.View>
</ListView>
The Xaml behind code:
private Search _ruleSearch = new Search();
public Search RuleSearch { get { return _ruleSearch; }}
In Search class:
public ObservableCollection<Result> FileMatches { get; private set; }
Note the changes are made on a new thread, if that makes a difference:
private void FindResultOnNewThreads()
{
FileMatches.Clear();
Parallel.ForEach(_fileList, file =>
{
foreach (Regex search in SearchTermList.Where(search => search.IsMatch(file)))
{
lock (FileMatches)
{
FileInfo fileInfo = new FileInfo(file);
FileMatches.Add(new Result
{
Attributes = fileInfo.Attributes,
DirectoryName = fileInfo.DirectoryName,
Extension = fileInfo.Extension,
FileName = fileInfo.Name,
FullNamePath = fileInfo.FullName,
Size = fileInfo.Length
});
}
}
});
}
Result class:
public class Result
{
public string FileName { get; set; }
public string DirectoryName { get; set; }
public string FullNamePath { get; set; }
public long Size { get; set; }
public string Extension { get; set; }
public FileAttributes Attributes { get; set; }
}
Issue really is, I am learning wpf by myself and couldn't really find a rule set for data binding in WPF. I know that it requires properties and public ones, other than that I am stuck.
Probably your listViews DataContext is not set to the UserControls or Windows or whatever, where you have your RuleSearch prop.
You can set it in the xaml.cs codebehind.
lvResult.DataContext = this;
or in xaml
<ListView Height="117" HorizontalAlignment="Left" Margin="20,239,0,0" Name="lvResults" VerticalAlignment="Top" Width="759" ItemsSource="{Binding Path=RuleSearch.FileMatches, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding FileName}"/>
<GridViewColumn Header="Size" Width="120" DisplayMemberBinding="{Binding DirectoryName}"/>
<GridViewColumn Header="Date" Width="120" DisplayMemberBinding="{Binding Size}"/>
<GridViewColumn Header="Full Path" Width="120" />
<GridViewColumn Header="Some Meaningless Data" Width="120" />
</GridView>
</ListView.View>
</ListView>
where typeOfAncestor is the type of your usercontrol/window...
Your Search class needs to implement INotifyPropertyChanged, which is used to let the UI know when the properties in your code-behind changes (in this case, FileMatches). This will involve registering a callback to the FileMatches.CollectionChanged event that then raises the INotifyPropertyChanged.PropertyChanged event with a property name of "FileMatches".
Note that if you ever want the values in the collection to stay in sync with the UI, you should also implement INotifyPropertyChanged on your Result class. (From looking at your code, the contents of your Result class looks constant, so you won't need to do this).
Related
What i have:
i have a ListView with 6 Values(string) and a Name(string). I also added a Checkbox for each row.
enter image description here
Struct:
public struct ststuff
{
public bool bEnable { get; set; }
public string sone { get; set; }
public string stwo { get; set; }
public string stree { get; set; }
public string sa { get; set; }
public string sb { get; set; }
public string sc { get; set; }
public string sName { get; set; }
}
Collection:
private ObservableCollection<ststuff> _astuff;
Some Code:
<ListView Margin="10,100,10,10" Name="lvstuff" ItemsSource="{Binding astuff,Mode=TwoWay,ElementName=window}" SelectedIndex="{Binding nIndex,Mode=TwoWay,ElementName=window}">
<ListView.View>
<GridView>
<GridViewColumn Header="[]">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding bEnable,ElementName=window}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name" Width="150" DisplayMemberBinding="{Binding sName}"/>
<GridViewColumn Header="a" Width="100" DisplayMemberBinding="{Binding sa}" />
<GridViewColumn Header="b" Width="100" DisplayMemberBinding="{Binding sb}" />
<GridViewColumn Header="c" Width="100" DisplayMemberBinding="{Binding sc}" />
<GridViewColumn Header="1" Width="100" DisplayMemberBinding="{Binding sone}" />
<GridViewColumn Header="2" Width="100" DisplayMemberBinding="{Binding stwo}" />
<GridViewColumn Header="3" Width="100" DisplayMemberBinding="{Binding stree}" />
</GridView>
</ListView.View>
</ListView>
Now what i want:
Click on one check box and save true/false in a a value.(e.g. _astuff[0].bEnable for first checkbox)
Atm. if i click on one box all boxes goes check/uncheck because i binded all to the same value.
Somehow i want to bind IsChecked Property of Checkbox to bEnable of my struct but i dont know how!
I got it maybe not the best way but it works.
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBox cb = e.OriginalSource as CheckBox;
int index = _astuff.IndexOf((ststuff)(cb).DataContext);
ststuff stuff= _astuff[index];
stuff.bEnable = true;
_astuff[index] = stuff;
}
The problem is that the selected index of ListView dont change by check/uncheck a checkbox. In this way you can find the index in your collection. You have to do the same with Uncheck event.
I have a listview with id, name and price. Each such an item has a sublist with categories.
So "for each" Item I want to display all subitems.
This is would it should look like:
But I don't know how to do it.
Here is my xaml code.
<ListView.View>
<GridView>
<GridViewColumn Width="140" Header="ID" DisplayMemberBinding="{Binding ID}"/>
<GridViewColumn Width="140" Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="300" Header="Price" DisplayMemberBinding="{Binding Price}" />
</GridView>
<!-- ??? -->
<Gridview ItemSource="{Binding Childs}">
I have to add a subgrid I think, but I don't know how.
This is my class structure
public class GroupedItem
{
public int ID { get; set; }
public string Name { get; set; }
public float Price { get; set; }
public IEnumerable<Item> Childs { get; set; }
}
Has someone an idea?
you can do this with datagrid :
<w:DataGrid ItemsSource={Binding Source={StaticResource GroupedItemList}}>
<w:DataGrid.RowDetailsTemplate>
<DataTemplate>
<w:DataGrid ItemsSource={Binding Childs }>
</w:DataGrid>
</DataTemplate>
</w:DataGrid.RowDetailsTemplate>
<w:DataGrid.Columns>
<w:DataGridTextColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<w:DataGridTextColumn Header="Price" DisplayMemberBinding="{Binding Price}" />
</w:DataGrid.Columns>
Also you can use this datagrid in codeplex it's free
WPF Extended DataGrid
You might want to use so-called tree-grid-view. There are really cool proprietary implementations by DevExpress and Infragistics. But also there are free soultions, descussed here
In your listviewitem you will have to build a template with an other listview
so your second listview is bind to
public IEnumerable<Item> Childs { get; set; }
I am trying to bind the List View to Observable Collection of a class and for some reason, the DisplayMemberBinding attribute of GridViewColumn is not binding to the content of collection.
If I use ListView.ItemTemplate, everything works fine. But I need the data in the form of a grid, so I am using GridView inside ListView.View.
First Image, Image 1 is the AssessmentSummaryList content in debug mode.
Second Image, Image 2 is the final output shown on the screen. The content from the list is not binding to the GridViewColumn.
These are the two view models I am using:
public class AssessmentSummaryViewModel : BaseViewModel
{
public string Question { get; set; }
public string Title { get; set; }
public string SelectedOption { get; set; }
public int Id { get; set; }
}
public class AssessmentViewModel : BaseViewModel
{
private ObservableCollection<AssessmentSummaryViewModel> assessmentSummaryList;
public ObservableCollection<AssessmentSummaryViewModel> AssessmentSummaryList
{
get { return assessmentSummaryList; }
set
{
assessmentSummaryList = value;
NotifyPropertyChanged("AssessmentSummaryList");
}
}
public void SetNextAssessment()
{
AssessmentSummaryList= Service.GetAssessmentSummary(ApplicationModel.SelectedModule.Id,
ApplicationModel.SelectedUtility.Id); //the service returns ObservableCollection<AssessmentSummaryViewModel> data
var EndScreen = new AssessmentSummaryView(); //Setting the last screen to AssessmentSumamryView, this will be called dynamically from other UserControl Xaml Page
}
}
AssessmentSummaryView Xaml Code is as follows:
<ListView x:Name="lstvSummary1" Grid.Row="2" ItemsSource="{Binding Path=DataContext.AssessmentSummaryList, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource myHeaderStyle}">
<GridViewColumn DisplayMemberBinding="{Binding Path=Title}" Width="200">
<GridViewColumnHeader Content="Design Element" />
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=Question}" Width="400">
<GridViewColumnHeader Content="Question" />
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Path=SelectedOption}" Width="300">
<GridViewColumnHeader Content="Response" />
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
AssessmentSummaryView Code-behind is as follows:
public partial class AssessmentSummaryView : UserControl
{
public AssessmentSummaryView()
{
InitializeComponent();
}
}
Try something like:
<ListView ItemsSource="{Binding Path=DataContext.AssessmentSummaryList, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Design Element" Width="200" DisplayMemberBinding="{Binding Path=Title}"/>
<GridViewColumn Header="Question" Width="400" DisplayMemberBinding="{Binding Path=Question}"/>
<GridViewColumn Header="Response" Width="300" DisplayMemberBinding="{Binding Path=SelectedOption}"/>
</GridView.Columns>
</GridView>
</ListView.View>
I have a ListView called lv with three columns. What's the quickest and easiest way to add items in it during runtime? I am using WPF.
Try this:
<ListView
x:Name="lv"
ItemsSource="{Binding Path=Items}"
SelectedItem="{Binding Path=SelectedItem}">
<ListView.View>
<GridView >
<GridViewColumn Header="Header1" DisplayMemberBinding="{Binding Path=Prop1}" />
<GridViewColumn Header="Header2" DisplayMemberBinding="{Binding Path=Prop2}"/>
<GridViewColumn Header="Header3" DisplayMemberBinding="{Binding Path=Prop3}"/>
</GridView>
</ListView.View>
</ListView>
In your ViewModel you should have some collection, like this:
public ObservableCollection<Test> Items { get; protected set; }
where Test is :
public class Test
{
public int Prop1{ get; set; }
public String Prop2{ get; set; }
public int Prop3{ get; set; }
}
When you will put/remove data in this "Items" Property, ListView will update itself automaticly.
I have 2 collections I want to bind to a separate GridViewColumn in a ListView:
public class EffectView : INotifyPropertyChanged
{
ObservableCollection<Effect> effects;
public ObservableCollection<Effect> Effects
{
get { return this.effects; }
set
{
this.effects = value;
this.RaisePropertyChanged ( "Effects" );
}
}
ObservableCollection<EffectDescription> descriptions;
public ObservableCollection<EffectDescription> Descriptions
{
get { return this.descriptions; }
set
{
this.descriptions = value;
this.RaisePropertyChanged ( "Descriptions" );
}
}
}
I can do this:
<ListView ItemsSource="{Binding EffectView.Effects}">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Name}"
Header="Name" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Opacity}"
Header="Opacity" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding ?}"
Header="Description" />
</GridView>
</ListView.View>
</ListView>
But then everything is scoped to EffectView.Effects, but I want the default scope to be EffectView so I can easily assign multiple collections to the ListView.
Something like:
<ListView ItemsSource="{Binding EffectView}">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Effects Path=Name}"
Header="Name" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Effects Path=Opacity}"
Header="Opacity" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Descriptions Path=Usage}"
Header="Description" />
</GridView>
</ListView.View>
</ListView>
Any way to accomplish this?
The ItemsSource of a ListView is the collection whose items will appear in the list. So think for a moment about what you're asking the ListView to do:
As a source collection, use something that isn't a collection, but contains them
For each row in the list, display an item from one collection, and an item from the other collection
Pay attention to that second point: every row must display some item from the Events collection and some item from the Descriptions collection.
Which item shall it pick from each? What's the relationship between the items in the two collections?
It appears that what you really need is a collection of objects that contains both an Event and a Description. You can then bind to that collection to display elements of both entities. Roughly, something like this:
public class EffectView : INotifyPropertyChanged
{
ObservableCollection<EffectsAndDescriptions> effects;
public ObservableCollection<EffectAndDescriptions> Effects
{
get { return this.effects; }
set
{
this.effects = value;
this.RaisePropertyChanged ( "EffectsAndDescriptions" );
}
}
}
internal class EffectsAndDescriptions
{
public Effect Effect { get; set; }
public Description Description { get; set; }
}
Now you can bind to the EffectsAndDescriptions collection (note this assumes the DataContext of the ListView's parent is EffectView)
<ListView ItemsSource="{Binding EffectsAndDescriptions}">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Effect.Name}"
Header="Name" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Effect.Opacity}"
Header="Opacity" />
<GridViewColumn Width="Auto"
DisplayMemberBinding="{Binding Description.Usage}"
Header="Description" />
</GridView>
</ListView.View>
</ListView>
That's not really possible. ItemsSource expects to be passed something that, at a minimum, implements IEnumerable. Even if you implemented IEnumerable on your EffectView, you would need to wrap Effect and EffectDescription into a single object to return.
You could bind your ListView to your Effects collection, then use a custom IValueConverter to take the Effect and return a description. But I'm not sure this would fit your situation.
Your best bet would be to include a wrapper object (i.e. EffectWithDescription) that includes both your Effect and your EffectDescription. Then expose a collection of EffectWithDescription objects. You'd need to ensure that this new collection is keep in sync with the other collections though.