WPF stackpanel does not show the whole content - c#

I am using a Combobox to show the contents of two different Lists, the List<Production> productions and the List<Seasons> seasons. Below is the XAML code
<StackPanel Grid.Column=" 1" Grid.Row="2">
<ComboBox x:Name="comboBox" ItemsSource="{Binding ShowProductionsSeasonsNames}" SelectedItem="{Binding SelectedProductionsSeasons}">
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="14"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
And this is the code in the viewmodel.
public ListCollectionView ShowProductionsSeasonsNames
{
get
{
List<ProductionsSeasonsNames> items = new List<ProductionsSeasonsNames>();
foreach (var production in this.Productions)
{
items.Add(new ProductionsSeasonsNames() { Name = production.DisplayName, Category = "Productions" });
}
foreach (var seasons in this.Seasons)
{
items.Add(new ProductionsSeasonsNames() { Name = seasons.DisplayName, Category = "Seasons" });
}
ListCollectionView lcv = new ListCollectionView(items);
lcv.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
return lcv;
}
}
public class ProductionsSeasonsNames
{
public string Name { get; set; }
public string Category { get; set; }
}
My issue is that it partly works. Although my productions and seasons lists contain around 100 items each, in the stackpanel only 15 from each list appear. Furthermore when I scroll down or up the focus goes directly to the top of each header without letting me to actually scroll (I am sorry I cant explain this part better). Does anyone have any ideas on this?

Use Grid instead of Stackpanel and set grid height="*" or you can directly put your combobox into Grid.Column=" 1" Grid.Row="2"

Related

Moving items in ItemsControl [duplicate]

This question already has answers here:
WPF 4 Drag and Drop with visual element as cursor
(2 answers)
Closed 1 year ago.
I have a list of items, which every item include a few of notes, like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Item> items = new List<Item>();
Item item1 = new Item() { Title = "First Item" };
Item item2 = new Item() { Title = "Second Item" };
item1.ItemNotes.Add(new ItemNote() { Note = "first_note" });
item1.ItemNotes.Add(new ItemNote() { Note = "second_note" });
item1.ItemNotes.Add(new ItemNote() { Note = "third_note" });
item2.ItemNotes.Add(new ItemNote() { Note = "fourth_note" });
item2.ItemNotes.Add(new ItemNote() { Note = "fifth_note" });
items.Add(item1);
items.Add(item2);
ic_tasks.ItemsSource = items;
}
}
public class Item
{
public Item()
{
this.ItemNotes = new ObservableCollection<ItemNote>();
}
public string Title { get; set; }
public ObservableCollection<ItemNote> ItemNotes { get; set; }
}
public class ItemNote
{
public string Note { get; set; }
}
In XAML I present those items by ItemsControl, like this:
<Grid>
<ItemsControl Name="ic_tasks">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:Item}">
<StackPanel Orientation="Vertical">
<Border BorderThickness="1" CornerRadius="8" Background="DarkGray">
<StackPanel Orientation="Horizontal" Height="30">
<TextBox Text="{Binding Title}" Margin="5,5,5,5"/>
<Button Margin="5,5,5,5">Fold</Button>
</StackPanel>
</Border>
<ItemsControl ItemsSource="{Binding ItemNotes}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ItemNote}">
<TextBox Margin="40,5,5,5" Text="{Binding Note}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
And now my goal is to make those Items moveable. For example, I would like to be able to move the second item over the first item, like below:
What I tried?
I try to wrap all ItemsControl by Canvas and bind position by Canvas.Top & Canvas.Left by using Mouse Events, but there's no such property for StackPanel which I want to move. There's only such propery for ItemsControl, like below:
<Canvas x:Name="canvas_of_items">
<ItemsControl Name="ic_tasks" Canvas.Top="{Binding YPosition}" Canvas.Left="{Binding XPosition}">
<...>
<StackPanel Orientation="Horizontal" Height="30" PreviewMouseDown="StackPanel_MouseDown" PreviewMouseMove="StackPanel_MouseMove" PreviewMouseUp="StackPanel_MouseUp">
<...>
</ItemsControl>
</Canvas>
So my question is:
What are the options to allow Items to be moved between themselves? Importantly, when moving the Item, it should be visible, not the mouse cursor itself.
You can set the ItemsControl to place your items in a canvas using ItemsControl.ItemsPanel like this:
<ItemsControl Name="ic_tasks">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas></Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
But to move the items you cant just set the Canvas properties on the StackPanel because they are surrounded by ContentPresenter. Instead you use ItemsControl.ItemContainerStyle to set a style on these ContentPresentors and there you can do something like this:
<Style>
<Setter Property="Canvas.Left" value="<<somthing - propebly a binding so it will be easy to change>>"
</Style>

wpf - listview inside of tabcontrol

I'm having difficulties to Binding a ListView inside of a TabControl
Bellow is my ViewModel
public ObservableCollection<TabItem> Tabs { get; set; }
public class TabItem
{
public string Header { get; set; }
public ObservableCollection<abc> Content { get; set; }
}
public class abc
{
public string text { get; set; }
}
this is how I initialize it
InitializeComponent();
Tabs = new ObservableCollection<TabItem>()
{
new TabItem {
Header = "Header1",
Content = new ObservableCollection<abc>()
{
new abc{ text = "content1" },
new abc{ text = "content2" },
}
},
new TabItem {
Header = "Header2",
Content = new ObservableCollection<abc>()
{
new abc{ text = "content3" },
new abc{ text = "content4" },
}
}
};
tabControl1.ItemsSource = Tabs;
and here is how I populate and Bind it in the view
<TabControl Name="tabControl1">
<TabControl.ItemTemplate>
<!-- this is the header template-->
<DataTemplate>
<TextBlock Width="500" Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!-- this is the body of the TabItem template-->
<DataTemplate>
<ListView Margin="10" ItemsSource="{Binding Content}">
<ListViewItem>
<StackPanel>
<TextBlock Text="{Binding text}" />
</StackPanel>
</ListViewItem>
</ListView>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
but then I got an errror saying that
InvalidOperationException: Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.
How can I bind a ListView inside a TabControl?
Each Tab control will have the same format of ListView. The Actual ListView will have more than 1 field. not only text field.
Where did I do it wrongly?
ListViewItems are created by ListView for each element in ItemsSource. To change their look you need ItemTemplate:
<ListView Margin="10" ItemsSource="{Binding Content}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding text}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
if you have more than 1 field, not only text field, define GridView with columns like shown in this post

WPF C# add text and image to ComboBox during runtime

Basically, when a user clicks a button I would like to add a list of the names of the currently running applications with their icon next to them within a ComboBox. I know how to get my list of applications and my icons but am unsure how to link everything together. My XAML for the ComboBox currently looks like the following:
<ComboBox x:Name="dial1AppSelection" Grid.Column="3" Grid.Row="4" MinHeight="25" Height ="25" MaxHeight="35" MinWidth="120" Margin="4,0,0,0" SelectionChanged="dial1AppSelection_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="20" Source="???" />
<TextBlock ><Run Text="???" /></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Add to your combobox
ItemsSource="{Binding YourCollection}"
Create class for your objects:
public class MyProcess
{
public ImageSource Image { get; set; }
public string Text { get; set; }
}
Add such code to your MainWindow.xaml.cs
public ObservableCollection<MyProcess> YourCollection { get; set; }
public MainWindow()
{
InitializeComponent();
YourCollection = new ObservableCollection<MyProcess>();
YourCollection.Add(new MyProcess() { Image ="image1", Text = "txt1"});
DataContext = this;
}
Insert fields names to your xaml code:
Source="{Binding Path=Image}"
Text="{Binding Path=Text}"

TextBlock not showing up in ListView

This is my ListView:
<ListView ItemsSource="{Binding ModuleList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!--<Image Source="{Binding ModuleImage}" Stretch="UniformToFill"/>-->
<TextBlock Text="{Binding ModuleName}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is the code in WPF (MVVM):
public ItemListViewVM() : base()
{
ModuleList = new List<Module>();
modulLoader.LoadAllModules();
tempModulList = modulLoader.GetAllModules();
foreach (var module in tempModulList)
{
ModuleImage = module.ModuleImage;
ModuleName = module.Name;
ModuleList.Add(module);
}
}
Long story short: The List tempModulList contains Objects of type Module, which has an ImageSource Image and a string Name. Then the ModuleList gets one item after another. When I uncomment the Image in xaml, you can see it. But the TextBlock won't show up no matter what. I checked the string module.Namefor every item, it is not empty.
EDIT: Add Module Class
The Module Class just contains Name and Image:
public class Module
{
public ImageSource ModuleImage { get; set; }
public string Name { get; set; }
}
The Object gets created by deserializing a Json
The Bindings in the ItemTemplate use the properties of the data item class as their source properties. Hence you should write
Text="{Binding Name}"
instead of
Text="{Binding ModuleName}"
Unless you set the View property of a ListView (to e.g. a GridView) you could also better use a ListBox, which is the base class of ListView, and simpler:
<ListBox ItemsSource="{Binding ModuleList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ModuleImage}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Initializing the ModuleList property in the view model constructor would be as simple as this:
public ItemListViewVM()
{
modulLoader.LoadAllModules();
ModuleList = modulLoader.GetAllModules();
}

Combobox within a listbox and a related data binding issue

Alrighty, so I have, as I said, a ComboBox within a ListBox. These are both controls (WPF project using C# in Visual Studio 2010 and MVVM).
I can create them all good and well.
The ListBox contains an ObservableCollection of something called IntWrapper, which just contains and int.
The ComboBox is loaded with an ObservableCollection of something called MissionViewModel, which is just a class that contains a few basic data types.
So the ItemSource for the ListBox and the ComboBox are different.
I can add and items to the ListBox with a button, which just adds a new IntWrapper to the ObservableCollection. Visually this gives me a new ComboBox, which is populated.
What I can't figure out is how to get one of the properties of MissionViewModel to go to the property in the IntWrapper when I select it.
Previously I just used a TextBox in the ListBox, which looked like this:
<ListBox x:Name="lbMissionsToGive" ItemsSource="{Binding MissionsToGive}" SelectedItem="{Binding SelectedMissionToGive}" ContextMenu="{StaticResource RemoveMissionToGiveMenu}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="LightBlue" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" CornerRadius="5,5,5,5">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="1">Mission ID: </TextBlock>
<TextBox Margin="1" Text="{Binding Int, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My (non working) attempt whereby I replace the TextBox with the ComboBox is this:
<ListBox x:Name="lbMissionsToGive" ItemsSource="{Binding MissionsToGive}" SelectedItem="{Binding SelectedMissionToGive}" ContextMenu="{StaticResource RemoveMissionToGiveMenu}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="LightBlue" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" CornerRadius="5,5,5,5">
<StackPanel Orientation="Horizontal">
<ComboBox DisplayMemberPath="MissionNameAndID" SelectedValuePath="Int" ItemsSource="{Binding MissionListViewModel.MissionVMs, Source={StaticResource Locator}}"></ComboBox>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I had thought SelectedValuePath would be the thing I wanted, but this so far doesn't seem to work (perhaps I'm using it incorrectly).
Any assistance would be appreciated.
Bind the SelectedValue property of the ComboBox to the Int property of the IntWrapper:
<ComboBox DisplayMemberPath="MissionNameAndID" SelectedValuePath="Int" SelectedValue="{Binding Int}"
ItemsSource="{Binding MissionListViewModel.MissionVMs, Source={StaticResource Locator}}" />
This should work provided that the IntWrapper.Int property has a public setter and that the Int property of the MissionViewModel is of the same type as the Int property of the IntWrapper.
I tried to create a solution for your problem based on your description. To help you more qualified, we would need more information about the MissionViewModel and the Properties contained in it.
I suppose, your Binding does not point to the correct ViewModel. I tried to recreate what you describe below and in my case, the combobox is filled with the Name Property of the MissionVm class.
This is the XAML. Here I changed the ItemsSource Binding to directly point to the ObservableCollection of the DataContext of the parent window.
<ListBox x:Name="lbMissionsToGive" ItemsSource="{Binding MissionsToGive}" SelectedItem="{Binding SelectedMissionToGive}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="LightBlue" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" CornerRadius="5,5,5,5">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding }"></TextBlock>
<ComboBox Margin="20" Width="50" ItemsSource="{Binding DataContext.MissionVMs, RelativeSource={RelativeSource AncestorType=Window}}" DisplayMemberPath="Name"></ComboBox>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And this is the ViewModel based on your description:
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
var vm1 = new MissionVM { Id = 1, Name = "111"};
var vm2 = new MissionVM { Id = 2, Name = "222" };
var vm3 = new MissionVM { Id = 3, Name = "333" };
MissionVMs = new ObservableCollection<MissionVM> { vm1, vm2, vm3};
MissionsToGive = new ObservableCollection<int> {1, 2, 3};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<int> MissionsToGive { get; set; }
public int SelectedMissionToGive { get; set; }
public ObservableCollection<MissionVM> MissionVMs { get; set; }
}
public class MissionVM
{
public int Id { get; set; }
public string Name { get; set; }
}
This is the Output:
I hope this helps. If i misunderstood you, please give further explanation of the problem.

Categories