I have a WPF window that only has a ComboBox (drop down list). If I choose index 1 (second item on the drop down list), how can I extend that WPF window to show more buttons, textbox, etc? Would I need to use the selectedIndex property? If so how do I make the window extend in the XAML.
I have used a IValueConverter in the past to accomplish this. Here is a sample converter:
public class MyConverter : System.Windows.Data.IValueConverter {
public object Convert ( object value , Type targetType , object parameter , CultureInfo culture ) {
if ( value == null )
return System.Windows.Visibility.Hidden;
if ( parameter == null )
return System.Windows.Visibility.Hidden;
if ( value.ToString().Equals ( parameter ) )
return System.Windows.Visibility.Visible;
return System.Windows.Visibility.Hidden;
}
public object ConvertBack ( object value , Type targetType , object parameter , CultureInfo culture ) {
throw new NotImplementedException ( );
}
}
What this does is, it takes the value that is passed to it, I am expecting a number such as the SelectedIndex of an items control. I then compare it to the parameter that is passed. If they are equal, I return Visibility.Visible. In all other instances, I return Visibility.Hidden.
Now, you can take that and plug it into the XAML like this:
<Window 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">
<Window.Resources>
<local:MyConverter x:Key="vConv"/>
</Window.Resources>
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="25,52,0,0" VerticalAlignment="Top" Width="120">
<ComboBoxItem>Hidden</ComboBoxItem>
<ComboBoxItem>Visible</ComboBoxItem>
</ComboBox>
<Label x:Name="label" Content="Label" HorizontalAlignment="Left" Margin="219,92,0,0" VerticalAlignment="Top" Visibility="{Binding ElementName=comboBox, Path=SelectedIndex, Converter={StaticResource vConv}, ConverterParameter=1, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
You can see that we created an instance of our MyConverter class in the Window.Resources. When we use this in our binding, we can show/hide my label based on whatever index is selected. Now this is very basic and you can add a lot to this to get all the functionality you need, bu this should get you started.
You can use the MaxHeight and MaxWidth property of the window on selection of the comboBox Item. Like this:
On Selection Change event of combobox.Use this
MainWindow obj= new MainWindow();
if(mycombobox.SelectedIndex==0)
{
obj.MaxWidth="600";
obj.MinWidth="600";
}
if(mycombobox.SelectedIndex==1)
{
obj.MaxWidth="200";
obj.MinWidth="200";
}
or you also can do this
if(mycombobox.SelectedIndex==0)
{
this.MaxWidth="600";
this.MinWidth="600";
}
if(mycombobox.SelectedIndex==1)
{
this.MaxWidth="200";
this.MinWidth="200";
}
Related
Hi at the begining of this question i want to say that i'm beginer and thing i've done here could prolly be done better but i'm still learnig. i want to set a value of some variable that contains the actual width of grid, and whenever i resize my window obviously this width change and I don't know how to access it. What i want to do ? Just want my progress bar to scale up or down when resizing
Here is some code
<Window x:Class="Lista6.MainPage"
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:Lista6"
mc:Ignorable="d"
Title="MainPage" Height="650" Width="1000"
Background="#36393F"
ResizeMode="CanResizeWithGrip"
Icon="/img/Ikonka2.png"
WindowStyle="None">
///other xaml code
<ProgressBar Foreground="White"
Value="50"
BorderThickness="0"
Background="Gray"
Height="5"
Width="{Binding Path=ProgresBarWidth[0], Mode=OneWay}"/>
</StackPanel>
</Grid>
</Grid>
</Window>
ObservableCollection<double> width = new ObservableCollection<double> { };
public ObservableCollection<double> ProgresBarWidth
{
get
{
return width;
}
set
{
width = value;
}
}
public AppOperator()
{
width.Add(0);
}
private void ProgresbarWidthSetter()
{
MainPage mp = new MainPage();
width[0] = mp.MusicBar.Width;
}
Here as u can see i ve binded progres bar width to ObservColl property, and i want to call it when im resizing my window but i ve no idea how to do that.
Thanks for any help and wish u all good.
I have a ComboBox bound to a list of values, for the sake of this example, a simple array of ints. The control has a custom ItemTemplate assigned to format its elements, which, in turn, uses an IValueConverter for some of its operation.
There's a particular case where this fails: If the ComboBox has an element selected, and the list of items changes, the converter's Convert is invoked with an empty string, which is most definitely not one of the values bound to my control. (Note that I'm not talking about the ConvertBack method.)
My questions would be:
Why is the IValueConverter being invoked with value being an empty string ("") when no string is ever bound to the control?
What are some non-hacky solutions to the problem? (I could just place if (value is "") return null; in the converter, and it seems to make the error go away, but I feel like it's treating the symptoms, not the cause.)
The problem can be reproduced with a simple WPF project (dotnet new wpf) containing only these 3 files (the problem is present on both the Framework and Core versions of .NET):
MainWindow.xaml:
<Window x:Class="test.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:test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ComboBox x:Name="Selector" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={x:Static local:Converter.Instance}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="Replace" Click="Button_Click" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.Windows;
namespace test
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Selector.ItemsSource = new int[] { 1, 2, 3 };
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Selector.ItemsSource = new int[] { -1, -2, -3 };
}
[STAThread]
public static void Main(String[] args)
{
new MainWindow().ShowDialog();
}
}
}
And finally Converter.cs:
using System;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Data;
namespace test
{
class Converter : IValueConverter
{
public static Converter Instance { get; } = new Converter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.Assert(value is int);
return (2 * (int) value).ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}
These 3 files form a minimal example that exhibits this behavior. If the "replace" button is clicked without selecting anything from the ComboBox first, the program runs fine. If, however, any value is selected in the ComboBox before clicking the button, the assertion in the converter fails, because upon clicking the button, the converter is passed a value of "".
I Tried your code when a currently selected item is not in the newly set item source the selected item should be changed right? So they change the selected item to "". That is the selected item you see in the initial state of the app running.
For a fact, if you keep the selected item in the new item source then it won't execute convert with value "".
Consider your initial item source is
Selector.ItemsSource = new int[] { 1, 2, 3 };
Now you select the second element which is displayed as 4 in your combo box and click replace to run this.
Selector.ItemsSource = new int[]{ -1, 2, -3 };
Then it won't execute the converter with value "".
Here the problem is the selected item is not present in the new item source so the Combobox is selecting its default value and your Combobox will behave like the exact way you started your application.
I'm using a ListBox in combination with a ObservableCollection. The content is set via a TemplateSelector (TextBlock or Label). The text has to be formatted (f.e. with Run-Tags in Code-behind), but i can't access the Items. Is there a solution to get the elements?
I've tried the usage of OfType<>, but this works only on Panels. I searched for an children-attribute but, there isn't one for ListBoxes. Setting the Name-Property via binding is not possible for UId and Name.
An IEnumerator for the LogicalChildren doesn't work and iterate over the whole content everytime a new element is added, is not so optimal. Here a minimal example.
<Window.Resources>
<DataTemplate x:Key="TextBlockTemplate">
<StackPanel>
<TextBlock />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="LabelTemplate">
<StackPanel>
<Label/>
</StackPanel>
</DataTemplate>
<local:myTemplateSelector x:Key="myTemplateSelector" x:Name="myTemplateSelector" TextBlockTemplate="{StaticResource TextBlockTemplate}" LabelTemplate="{StaticResource LabelTemplate}"/>
</Window.Resources>
<Grid Margin="0">
<ListBox Name="mylist" Grid.Row="3"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding _listEntries}"
ItemTemplateSelector="{StaticResource myTemplateSelector}"
>
</ListBox>
</Grid>
Greetings and thanks :)
The TextBlock has an Inlines property that returns the Inline elements that comprise the contents of the TextBlock.
The Label has a Content property that you, depending on how you are using it, may cast to a Panel.
There are no inline elements for a TextBox.
Now, I found a solution. I made the TextBlock and Label as User Control and set the Name-property. In the code-behind, I have access to the DataContext and the element can set itself.
<UserControl x:Class="Test.TextBlockControl"
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"
xmlns:local="clr-namespace:TextBlockControl"
Loaded="UserControl_Loaded">
<Grid>
<StackPanel HorizontalAlignment="Stretch" Margin="0,0,0,0">
<TextBlock Name="textBlock"/>
</StackPanel>
</Grid>
In the Code behind i can now access the values and set:
public partial class TextBlockControl : UserControl
{
public List<string> name => DataContext as List<string>;
public TextBlockControl()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
foreach (var t in name)
{
var run = new Run(t.Text);
if (t.IsHighlighted)
{
run.Foreground = Brushes.Green;
}
else
{
run.Foreground = Brushes.Red;
}
textBlock.Inlines.Add(run);
}
}
}
}
In the MainWindow, the dataTemplate then references the UserControl (root is the namespace):
<root:PickControl />
Based on what I have read online binding to an array shoud be as simple as setting the DataGrid.temsSource property to point to the array but when I do that I only see the length of each item in the array.
Here is what I have...
XAML:
<Window x:Class="DataGridTesting.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:DataGridTesting"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid x:Name="hobbiesDataGrid"
HorizontalAlignment="Left" Margin="118,84,0,0" VerticalAlignment="Top" Height="138" Width="360">
</DataGrid>
</Grid>
</Window>
Code Behind:
namespace DataGridTesting
{
public partial class MainWindow : Window
{
string[] hobbies = new string[] { "Computers", "Cars", "Photography" };
public MainWindow()
{
InitializeComponent();
this.hobbiesDataGrid.ItemsSource = hobbies;
}
}
}
Output:
Length
9
4
11
What I'm expecting to see in the first column is
hobbies
Computers
Cars
Photography
What am I missing? How can I properly show the items in the array on the first column of the DataGrid?
You need to set your grid up to not autogenerate columns as there is no context in the array.
<DataGrid x:Name="dataGrid" ItemsSource="{Binding}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="128" Width="273" Margin="76,94,0,0" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Hobbies" Binding="{Binding}" />
</DataGrid.Columns>
</DataGrid>
Also notice the ItemsSource is set to binding and Binding is set in the DataGridTextColumn.
You can't use string array directly.
Check the below code:
DataTable hobbies = new DataTable();
hobbies.Columns.Add("hobbies");
hobbies.Rows.Add(new object[] { "Computers" });
hobbies.Rows.Add(new object[] { "Cars" });
hobbies.Rows.Add(new object[] { "Photography" });
Then use it like before:
this.hobbiesDataGrid.ItemsSource = hobbies;
All is explained here: http://www.psworld.pl/Programming/BindingListOfString
I'm a beginner at C#. Trying to follow this tutorial : https://www.youtube.com/watch?v=6OwyNiLPDNw
I've currently created a TreeView to display all the folders/files in the system as described in the above video. I've added a valueConverter class to change the icon based on if the item is a drive/folder/file. Have three files for that accordingly. Now, the problem, I'm facing is that, when I run the program, there's no exception thrown, but the images are not displayed. If I add a static image file in the corresponding xaml file, I see that it's displayed. But if I used a valueConverter, it's not. I single stepped into the program, and I can see that my valueConverter function is being successfully called and the corresponding image is chosen as well. I don't understand why the image is not being displayed.
Any help is highly appreciated !
Code for my xaml:
<Window x:Class="Wpf_TreeView.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:Wpf_TreeView"
Loaded="Window_Loaded"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TreeView x:Name="FolderView">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="100" Margin="3"
Source="{Binding
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TreeViewItem}},
Path=Tag,
Converter={x:Static local:HeaderToImageConverter.Instance}}"/>
<TextBlock VerticalAlignment="Center" Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
Code for my Value Converter:
namespace Wpf_TreeView
{
/// <summary>
/// Convert a full path to a specific image type of a drive,folder or a file
/// </summary>
[ValueConversion(typeof(string), typeof(BitMapImage))]
public class HeaderToImageConverter : IValueConverter
{
public static HeaderToImageConverter Instance = new HeaderToImageConverter();
//public static HeaderToImageConverter Instance { get => instance; set => instance = value; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//Get the full path
var path = (string)value;
//Check if path is null
if (path == null) return null;
//Get the name of the file/folder
var name = MainWindow.GetFileFolderName(path);
// By default we presume file image
var image = "Images/file.png";
// If the name is blank, we assume it's a drive (file/folder) cannot have blank name
if (string.IsNullOrEmpty(name))
{
image = "Images/drive.png";
}
else if (new FileInfo(path).Attributes.HasFlag(FileAttributes.Directory))
{
image = "Images/folder.jpg";
}
//the below statement does not seem to work although image contains the proper string as expected
return new BitmapImage(new Uri($"pack://application:,,,/{image}"));
}
}
}
The solution was to use BitmapImage instead of BitMapImage. Should be wary of using alt+enter without paying attention to what it does !