I have a problem with my List View.
It shows all the elements that I add to the ObservableCollection binded to it, just how it's supposed to work, but when I right-click any of it's elements, the bindings won't work and it won't display the data as I intend it to do.
I created another WPF project to show you the problem more clearly.
Here's my wpf code:
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView x:Name="listViewWithContextMenu" ItemsSource="{Binding Path=CollectionOfThings}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ListView.View>
<GridView>
<GridViewColumn Width="120" Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Width="120" Header="Quantity" DisplayMemberBinding="{Binding Quantity}"/>
</GridView>
</ListView.View>
<ListView.ContextMenu>
<ContextMenu>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical" Margin="3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name: "></TextBlock>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Quantity: "></TextBlock>
<TextBlock Text="{Binding Quantity}"></TextBlock>
</StackPanel>
</StackPanel>
</StackPanel>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
</Grid>
and the c# code behind it:
using System.Windows;
using System.Collections.ObjectModel;
namespace WpfApp2
{
public partial class MainWindow : Window
{
public ObservableCollection<DataOfThing> CollectionOfThings = new ObservableCollection<DataOfThing>();
public MainWindow()
{
InitializeComponent();
CollectionOfThings.Add(new DataOfThing() { Name = "Some Name", Quantity = 2 });
CollectionOfThings.Add(new DataOfThing() { Name = "Some Other Name", Quantity = 3 });
CollectionOfThings.Add(new DataOfThing() { Name = "Strange Name", Quantity = 1 });
listViewWithContextMenu.ItemsSource = CollectionOfThings;
}
}
public class DataOfThing
{
public string Name { get; set; }
public int Quantity { get; set; }
}
}
And here's what I get:
What happens is that ContextMenu is not in the same visual tree of your ListView (or any other control). It is completely separated from your Window element tree and that's why it gets lost on binding.
I got a solution that might not be the most beautiful but works :)
Set a ContextMenuOpening event to your ListView:
<ListView x:Name="listViewWithContextMenu" ItemsSource="{Binding Path=CollectionOfThings}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ContextMenuOpening="listViewWithContextMenu_ContextMenuOpening">
And in your codebehind, do:
private void listViewWithContextMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
var list = sender as ListView;
list.ContextMenu.DataContext = list.SelectedItem;
}
Related
There is a scroll viewer that contains a list view and there is list view on the sroll viewr. and when scroll on the list view it doesn't scroll but when you scroll out the list view(on the area of scroll viewer that is not behind list view).How can I fix this?
My code:
<ScrollViewer Background="#111" HorizontalScrollBarVisibility="Disabled" FlowDirection="LeftToRight" Grid.Row="1" >
<Grid Grid.Row="1">
<StackPanel Background="#111" HorizontalAlignment="Center">
<ListView BorderThickness="2" HorizontalAlignment="Right" ItemsSource="{Binding MyList}">
//My codes
</ListView>
</StackPanel>
</Grid>
</ScrollViewer>
A ScrollViewer enables content to be displayed in a smaller area than its actual size. For scrolling all elements, you could try to refer to the following code.
<Window x:Class="ScrollDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ScrollDemo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<ScrollViewer>
<Grid Height="500">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<TextBlock Text="scroll"/>
<ScrollViewer Background="#111" HorizontalScrollBarVisibility="Disabled" FlowDirection="LeftToRight" Grid.Row="1" >
<ListView x:Name="lv" BorderThickness="2" HorizontalAlignment="Right" ItemsSource="{Binding }">
<ListView.View>
<GridView>
<GridViewColumn Width="100" Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Width="100" Header="Age" DisplayMemberBinding="{Binding Age}"/>
</GridView>
</ListView.View>
</ListView>
</ScrollViewer>
</Grid>
</ScrollViewer>
</Window>
MainWindow.xaml.cs:
using System.Collections.Generic;
using System.Windows;
namespace ScrollDemo
{
public partial class MainWindow : Window
{
List<Item> MyList;
public MainWindow()
{
InitializeComponent();
MyList = new List<Item>();
for (int i = 0; i < 10; i++)
{
MyList.Add(new Item(){ Name= $"list{ i }",Age=i });
}
lv.ItemsSource=MyList;
}
}
public class Item
{
public string Name { get;set;}
public int Age { get;set;}
}
}
The result
I have two user controls, one with input fields (Control A) and another one with Data Grid (Control B).
How can i display grid view selected item in textbox in WPF? Is it possible to do it only using WPF or must do inside the program code or both?
Control A with
<UserControl x:Class="PayrollSystem.Controls.EmployeeDetailControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock Text="First Name : " Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<StackPanel Margin="2" Grid.Column="1" Grid.Row="2" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox x:Name="txtFirstName" Text="{Binding Path=FirstName, Mode=TwoWay}" Width="200"/>
</StackPanel>
Control B with
<UserControl x:Class="PayrollSystem.Controls.EmployeeRecordControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataGrid x:Name="dg" AlternatingRowBackground="Gray" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName, Mode=TwoWay}"/>
</DataGrid.Columns>
EmployeeRecordControl seems redundant. Instead of that, just use a DataGrid directly, and bind its ItemsSource and SelectedItem to two properties of a view model class like this:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Employee> Employees { get; }
= new ObservableCollection<Employee>();
private Employee selectedEmployee;
public Employee SelectedEmployee
{
get => selectedEmployee;
set
{
selectedEmployee = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(SelectedEmployee)));
}
}
}
Then create and bind to the view model like this:
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<DataGrid ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee}"
CanUserAddRows="False"/>
<local:EmployeeDetailControl Grid.Column="1"
DataContext="{Binding SelectedEmployee}"/>
</Grid>
I am attempting to make a simple VS 2017 Extension that is taking a object and displaying it. I have the data coming back and displaying the json in a text box, so I know the data is coming back correctly. But for some reason the gv is just showing the word "id" twice, as their are two records in the dataset. I have tried so many things I'm loosing track. Plus the documentation seems to be all over the place.
I believe there could be at least 2 issues here...
1) XAML the "Bindings"
2) Binding or adding the data to the LV?
Any help with this would be greatly appreciated!
XAML
<UserControl x:Class="DoWork.AllWorkVSControl"
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"
Background="{DynamicResource VsBrush.Window}"
Foreground="{DynamicResource VsBrush.WindowText}"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Name="MyToolWindow">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Margin="10" HorizontalAlignment="Center">AllWorkVS</TextBlock>
<Button Content="Click me!" Click="button1_Click" Width="120" Height="80" Name="button1"/>
<TextBox Height="200" TextWrapping="Wrap" Name="txtJson"/>
<ListView x:Name="LvIssues">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Width="Auto" Header="Id" DisplayMemberBinding="{Binding Source='Id'}"></GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Grid>
</UserControl>
C#
public class People
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public partial class AllWorkVSControl : UserControl
{
public AllWorkVSControl()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
var t = Issues.GetPeopleById("2");
PopulateListView();
MessageBox.Show(t);
}
private void PopulateListView()
{
var l = GetPeople();
txtJson.Text = JsonConvert.SerializeObject(l);
foreach (var p in l)
{
LvIssues.Items.Add(p);
}
}
}
you need to set the ListView.ItemsSource.
private void PopulateListView()
{
var l = GetPeople();
txtJson.Text = JsonConvert.SerializeObject(l);
LvIssues.ItemsSource= l;
}
<ListView x:Name="LvIssues">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Width="Auto" Header="Id" DisplayMemberBinding="{Binding Id}"></GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
I'm tring to databind properties of an ObservableCollection to a ListBox (Just the Title property for example).
By clicking on one of the ListItem (with an event ), i'd like to display all the properties of the Collection into a StackPanel. After many tries, I still don't know how can I figure it out...
Here is my code behind :
public partial class TestListView : Window
{
public TestListView()
{
ObservableCollection<Programme> pgr = new ObservableCollection<Programme>();
pgr = readfile();
InitializeComponent();
}
public class Programme
{
public String Title { get; set; }
public String Date { get; set; }
public String Chaine { get; set; }
public Programme(String Title, String Date, String Chaine)
{
this.Title = Title;
this.Date = Date;
this.Chaine = Chaine;
}
}
Here is my XAML :
<Window x:Class="Test.TestListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test;assembly=Test"
Title="TestListView" Height="500" Width="1000" x:Name="Window">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="249*"/>
<ColumnDefinition Width="743*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20*"/>
<RowDefinition Height="428*"/>
<RowDefinition Height="21*"/>
</Grid.RowDefinitions>
<ListBox Name="l1" ItemsSource="{Binding pgr}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Column="1" Grid.Row="1">
<TextBox Margin="343,0,0,0" x:Name="Recherche"></TextBox>
<Button Height="37" Margin="669,0,0,0" ></Button>
<TextBlock x:Name="t1" Margin="214,0,293,0" Height="33" />
</StackPanel>
</Grid>
</Window>
You need to bind your data to (public) properties. Also, you don't need to use ObservableCollection; any selection changes will be picked up anyhow.
Here's a working sample, with layout and other bits and pieces changed to make it compile for me:
public partial class MainWindow
{
public IList<Programme> pgr { get; }
public MainWindow()
{
pgr = new List<Programme>
{
new Programme("First", "FirstDate", "FirstChaine"),
new Programme("Second", "SecondDate", "SecondChaine"),
new Programme("Third", "ThirdDate", "ThirdChaine"),
};
InitializeComponent();
}
public class Programme
{
// No changes
}
}
...and the XAML:
<Window
x:Name="self"
x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<StackPanel DataContext="{Binding ElementName=self}" Orientation="Horizontal">
<ListBox Name="l1" ItemsSource="{Binding pgr}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Orientation="Vertical">
<TextBox Margin="10" Text="{Binding ElementName=l1,Path=SelectedItem.Title}" />
<Button Height="37" Margin="0" Content="{Binding ElementName=l1,Path=SelectedItem.Date}"></Button>
<TextBlock Margin="10" Height="33" Text="{Binding ElementName=l1,Path=SelectedItem.Chaine}" />
</StackPanel>
</StackPanel>
</Window>
Note the bindings that reference the selected item:
Text="{Binding ElementName=l1,Path=SelectedItem.Title}"
I have some XAML I want to reuse. I can easily create a custom control and use it, but I'd rather not. Here's what I've tried:
<Window.Resources>
<Expander x:Key="Namespacer" x:Shared="False" Name="NS" Background="SkyBlue">
<StackPanel Name="ClientArea" Margin="20,0,20,0">
<StackPanel Name="Usings" Grid.Row="0" Height="Auto"></StackPanel>
<StackPanel Name="Structs" Grid.Row="1" Height="Auto"></StackPanel>
<StackPanel Name="Classes" Grid.Row="2" Height="Auto"></StackPanel>
<StackPanel Name="IFaces" Grid.Row="3" Height="Auto"></StackPanel>
<StackPanel Name="Delegates" Grid.Row="4" Height="Auto"></StackPanel>
<StackPanel Name="Enums" Grid.Row="5" Height="Auto"></StackPanel>
<StackPanel Name="Nested" Grid.Row="6" Height="Auto"></StackPanel>
</StackPanel>
</Expander>
</Window.Resources>
<StackPanel>
<ContentControl Name="N1" Content="{StaticResource Namespacer}" />
</StackPanel>
Now, I want to do something like:
this.N1.Header = "SomeTitle.Namespace1";
And also be able to add new chunks of XAML to my stack panels in N1 in a similar fashion. How to achieve that?
Well, you could do this:
((Expander)(this.N1.Content)).Header = "SomeTitle.Namespace1";
But that gets ugly. I'd recommend switching to data binding. Here's an example.
First, here's a data class with the structure I think you're going for:
public partial class MainWindow : Window
{
public class MyData
{
public string ItemTitle { get; set; }
public IList<string> Usings { get; set; }
public IList<string> Structs { get; set; }
}
public class MyViewModel
{
public IList<MyData> MyBoundData { get; set; }
}
public MainWindow()
{
var d1 = new MyData{
ItemTitle = "thing1",
Usings = new[]{"a", "b"}
};
var d2 = new MyData{
ItemTitle = "thing2",
Structs = new[]{"c","d"}
};
this.DataContext = new MyViewModel{
MyBoundData = new[]{ d1, d2}
};
InitializeComponent();
}
}
And here's an items control bound to our data:
<Window x:Class="WpfApplication1.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>
<ItemsControl ItemsSource="{Binding MyBoundData}" Focusable="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding ItemTitle}" Background="SkyBlue">
<StackPanel>
<Expander Header="Usings" Background="SkyBlue">
<ItemsControl ItemsSource="{Binding Usings}"/>
</Expander>
<Expander Header="Structs" Background="SkyBlue">
<ItemsControl ItemsSource="{Binding Structs}"/>
</Expander>
</StackPanel>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
Note that the items control has a DataTemplate that corresponds to your "Namespacer" xaml chunk. You could, of course, move the DataTemplate chunk into the window resources like you have in your example, if you want to use it in more than one ItemsControl.