Custom ItemControl for ListBox - wpf

I want to add new DependencyProperty to ListBoxItem class which used in ListBox control. I need to add IsEditing property. When user will does long click on my ListBoxItem I will change it IsEditing property to True and then in Trigger of this property will be style changing. Actually the idea to change TextBlock to TextBox in style.
I hope my explanation is clear. Does it make any sense, or there is exist more simple way?
Thanks

See the XAML below - A child of DataTemplate is your item's template. You can use built-in WPF elements like Grid, Canvas etc. or your own user/custom control. You can create EditableItem control and use it to handle long mouse clicks etc.
This is not the most elegant way of doing this, but probably the simplest one. The more advanced approach would involve the usage of attached behaviors, which elliminates need for custom/user controls. Also, look at VisualStateManager class, it'll simplify switching between different 'modes' of your editable item (that's what I'd use for it anyway).
<Window x:Class="WpfApplication2.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>
<ListBox x:Name="l">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="30" Background="Green" Width="100">
<TextBlock Text="{Binding}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += (o, e) =>
{
this.l.ItemsSource = Enumerable.Range(1, 3);
};
}
}
}

Related

MSVS - WPF - How do I make ProjectName.csproj build App.xaml and App.xaml.cs last?

My WPF project has App.xaml require other pieces of project code to be built before it gets built. Despite App.xaml's dependency on those classes and xaml files, Visual Studio wants to build it first before anything else can get built.
For further information, here's the dependencies that are not getting addressed when I build from scratch.
XAML namespaces:
xmlns:views="clr-namespace:ProjectName.Views"
xmlns:viewmodels="clr-namespace:ProjectName.ViewModels"
XAML namespace implementations:
<DataTemplate DataType="{x:Type viewmodels:HomeViewModel}">
<views:HomeView/>
</DataTemplate>
HomeViewModel is a class in ...ViewModels.
HomeView is a class and XAML file in ...Views.
I know that the .csproj build order is the main problem, because I built the solution omitting the datatemplates and got no errors, and when I built after putting them back in I am still fine.
But when I cleaned the solution and rebuilt, I get errors up the wazoo!
I am making an MVVM application and I absolutely need App.xaml to be built last, as otherwise it would be difficult to make further iterations (as in, more Views / ViewModels) to this project.
How do I alter the .csproj files so that this happens?
I'm using MS Visual Studio Community 2019.
Also, I tried looking through build targets and overriding them but I can't figure out how to make it so that certain pieces of source code get built last.
Additional context:
App.xaml
<Application x:Class="ProjectName.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ProjectName;assembly=ProjectName"
xmlns:views="clr-namespace:ProjectName.Views"
xmlns:viewmodels="clr-namespace:ProjectName.ViewModels"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!--
<DataTemplate DataType="{x:Type viewmodels:MainViewModel}">
<views:MainView/>
</DataTemplate>
-->
<DataTemplate DataType="{x:Type viewmodels:NGViewModel}">
<views:NGView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:HomeViewModel}">
<views:HomeView/>
</DataTemplate>
<!---->
</Application.Resources>
</Application>
HomeView.xaml.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ProjectName.Views
{
/// <summary>
/// Interaction logic for HomeView.xaml
/// </summary>
public partial class HomeView : UserControl
{
public HomeView()
{
InitializeComponent();
}
}
}
HomeView.xaml
<UserControl x:Class="ProjectName.Views.HomeView"
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:ProjectName.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="Black">
</Grid>
</UserControl>
HomeViewModel.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace ProjectName.ViewModels
{
public class HomeViewModel : BaseViewModel
{
}
}
BaseViewModel.cs (bonus - parent class of HomeViewModel)
using System;
using System.Collections.Generic;
using System.Text;
namespace Prototype7022.ViewModels
{
public class BaseViewModel
{
}
}

How can I bind the value cross the window?

Here is the XAML:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBox Text="{Binding CB+Width,RelativeSource={RelativeSource AncestorType={x:Type Window}},Mode=TwoWay}"></TextBox>
</Grid>
</Window>
And here is code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TB = new TextBox();
TB.Width = 200;
}
public ComboBox CB
{
get { return (ComboBox)GetValue(CBProperty); }
set { SetValue(CBProperty, value); }
}
// Using a DependencyProperty as the backing store for CB. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CBProperty =
DependencyProperty.Register("CB", typeof(ComboBox), typeof(MainWindow), null);
}
}
In fact, the DependencyProperty is a custom control, but I replace it as a ComboBox for easy to explain my problem to you.
The custom control is not in the same window and I want to bind its value such as Width to the TextBox.
The custom control is in a different window. That means there are two windows, window 1 and window 2. The custom control is in window 1 and the textbox is in window 2. I do this because one is the main window and the other is the setting window. Whenever the setting window changes the setting, you can see it in the main window at once. So I use a DependencyProperty to store the custom control which in a different window and wanna bind it to the textbox.
Now how can I do it? Would you please help me? Thank you.
It LOOKs like you want your textbox to show the width property of the Window? (or some other control in the future and just testing). If you apply an x:Name value to a control, you can reference that control directly in the XMAL and then properties directly on that. No need for any custom other control with your own dependency property... something like.
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
x:Name="myOuterWindowControl" >
<Grid>
<TextBox Text="{Binding ElementName=myOuterWindowControl, Path=Width, Mode=TwoWay,
UpdateSourceTrigger=LostFocus}" Height="30" Width="70"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
<TextBox Height="30" Width="70"
HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,50,0,0"/>
</Grid>
</Window>
Now I actually added an extra textbox control so there is more than one control in the XAML to allow focus to change. Reason. I set the textbox to allow mode as Two-Way binding. The window will push to the textbox, but you can also type in a value to the textbox and when the LOST FOCUS happens, it pushes the value back to the window and you can dynamically change the width (or other property height, coloring, whatever).
Is this more in line with what you were trying to get at?

Avoid dropdown selection change event during view load

Grid contains dropdown control. I want to avoid selection change event during view load. What is the best way to achieve that?
Well i don't know what dropdown control is in WPF
I have tried with combobox and SelectionChanged event in not firing at form load
Here's my code
<Window x:Class="Q6.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:Q6"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox SelectionChanged="ComboBox_SelectionChanged"
Height="30" Width="100">
<ComboBoxItem>Alpha</ComboBoxItem>
<ComboBoxItem>Beta</ComboBoxItem>
<ComboBoxItem>Gamma</ComboBoxItem>
</ComboBox>
</Grid>
</Window>
And the code-behind :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Q6
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//Not firing at load time
}
}
}

Contents of Content Control gone on tab switch

I am using mvvm. I am loading a usercontrol that contains a content control on two different tabs like so:
<TabControl>
<TabItem Header="View">
<StackPanel>
<Info:UserData/><!--UserData Control-->
<Button Content="View Entries" Command="{Binding BeginView}"/>
</StackPanel>
</TabItem >
<TabItem Header="Edit">
<StackPanel>
<Info:UserData/><!--UserData Control-->
<Button Content="Edit Entries" Command="{Binding BeginEdit}"/>
</StackPanel>
</TabItem >
</TabControl>
The User Control looks like:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{Binding UserTypeInfo}"/>
<Info:UserDetailsArea Grid.Row="1"/>
</Grid>
When the tab first loads the content of the ContentControl is set to an image. Depending on some actions the content may change to a datatable, video, etc. This part works fine.
When it loads the default tab is the first one. If I click on the second tab, you should see the same thing - with a different button, this works. But if I go back to the first tab the Content control is empty.
What do I need to do so that both tabs display the image?
The value bound to from the viewmodel as requested:
private object userTypeInfo
/// <summary>
/// User Specific data
/// </summary>
public object UserTypeInfo
{
get
{
return userTypeInfo;
}
private set
{
UuserTypeInfo= value;
OnPropertyChanged("UserTypeInfo");
}
}
Edit:
The following is a simplified example that I believe shows the same problem:
XAML code for window:
<Window x:Class="dualCC.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"
Loaded="Window_Loaded">
<Grid>
<TabControl>
<TabItem Header="One">
<StackPanel>
<Button Content="One" />
<ContentControl Name="CCone"/>
</StackPanel>
</TabItem>
<TabItem Header="Two">
<StackPanel>
<Button Content="Two" />
<ContentControl Name="CCtwo"/>
</StackPanel>
</TabItem>
</TabControl>
</Grid>
</Window>
Code behind (you'll need to fix the path to an image):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace dualCC
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Uri uri = new Uri(#"C:\Image.jpg");
BitmapImage temp = new BitmapImage(uri);
Image CurrentImage = new Image();
CurrentImage.Source = temp;
CCone.Content = CurrentImage;
CCtwo.Content = CurrentImage;
}
}
}
This isn't MVVM. In MVVM you never manipulate GUI elements directly in code-behind like this.
To answer your question, the problem is that you're creating an Image, which is actually a child control, and setting it as the content of two separate controls. Controls can only have one parent. Create separate Images instead and set the BitmapImage as the source for each:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Uri uri = new Uri(#"C:\Image.jpg");
BitmapImage temp = new BitmapImage(uri);
CCone.Content = new Image { Source = temp };
CCtwo.Content = new Image { Source = temp };
}
Or better yet use proper MVVM and do it with data binding.

silverlight binding combobox in nested controls

I have 2 user controls one named Filters and one named FilterItem
Filter looks like this:
<UserControl xmlns:my="clr-namespace:AttorneyDashboard.Views.UserControls" x:Class="AttorneyDashboard.Views.UserControls.Filters"
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:helpers="clr-namespace:AttorneyDashboard.Helpers"
mc:Ignorable="d"
d:DesignHeight="150" d:DesignWidth="590" x:Name="FiltersRoot">
<Grid>
<ListBox x:Name="myListBox" ItemsSource="{Binding Path=FilterItems, ElementName=FiltersRoot}" >
<ListBox.ItemTemplate>
<DataTemplate>
<my:FilterItem ColumnsList="{Binding Path=Columns_, ElementName=FiltersRoot}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Diagnostics;
using AttorneyDashboard.Helpers;
namespace AttorneyDashboard.Views.UserControls
{
public class MyItems
{
public string Header { get; set; }
}
public partial class Filters : UserControl
{
public Filters()
{
InitializeComponent();
}
private DependencyProperty FilterItemsProperty = DependencyProperty.Register("FilterItems", typeof(ObservableCollection<FilterDescriptor>), typeof(Filters), new PropertyMetadata(null, new PropertyChangedCallback(OnChangeFilterItems)));
public ObservableCollection<FilterDescriptor> FilterItems
{
get
{
return (ObservableCollection<FilterDescriptor>)GetValue(FilterItemsProperty);
}
set
{
SetValue(FilterItemsProperty, value);
}
}
public static void OnChangeFilterItems(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public List<MyItems> Columns_
{
get
{
List<MyItems> list = new List<MyItems>();
list.Add(new MyItems() { Header = "test1" });
list.Add(new MyItems() { Header = "test2" });
list.Add(new MyItems() { Header = "test3" });
return list;
}
}
}
}
FilterItems looks like this
<UserControl x:Class="AttorneyDashboard.Views.UserControls.FilterItem"
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"
d:DesignHeight="23" d:DesignWidth="590" xmlns:my="clr-namespace:AttorneyDashboard.Helpers" x:Name="FilterItemRoot">
<StackPanel Orientation="Horizontal">
<ComboBox Height="23" HorizontalAlignment="Left" Name="FieldName" VerticalAlignment="Top" Width="120" Margin="5,0,0,0" ItemsSource="{Binding Path=ColumnsList, ElementName=FilterItemRoot}" SelectedItem="{Binding PropertyPath, Mode=TwoWay}" DisplayMemberPath="Header"/>
</StackPanel>
</UserControl>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using AttorneyDashboard.Helpers;
using System.Windows.Data;
namespace AttorneyDashboard.Views.UserControls
{
public partial class FilterItem : UserControl
{
public FilterItem()
{
InitializeComponent();
}
private DependencyProperty ColumnsListProperty = DependencyProperty.Register("ColumnsList", typeof(List<MyItems>), typeof(FilterItem), new PropertyMetadata(null, new PropertyChangedCallback(OnChangeColumns)));
public List<MyItems> ColumnsList
{
get
{
return (List<MyItems>)GetValue(ColumnsListProperty);
}
set
{
SetValue(ColumnsListProperty, value);
}
}
public static void OnChangeColumns(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
}
The number of FilterItems is ok (this means that FilterItems binding works ok), but only the Combobox of the last FilterItem is populated...
And I do not know what exactly is wrong...
Update:
I found why but I stll do not know a solution...
It seams that the content of FilterItem is binded before his properties are..
So the combobox in FilterItem is binded before the Columns property is binded...
Your code in FilterItem
private DependencyProperty ColumnsListProperty = DependencyProperty
.Register("ColumnsList", typeof(List<MyItems>), typeof(FilterItem),
new PropertyMetadata(null, new PropertyChangedCallback(OnChangeColumns)));
Please, make it static:
private **static** DependencyProperty ColumnsListProperty = DependencyProperty
.Register("ColumnsList", typeof(List<MyItems>), typeof(FilterItem),
new PropertyMetadata(null, new PropertyChangedCallback(OnChangeColumns)));
Thats it.
P.S. : in Filters make dependency property static too, generally do it anywhere :)
You have placed a x:Name attribute directly on your UserControl elements. Don't do that. Use this pattern instead:-
<UserControl x:Class="AttorneyDashboard.Views.UserControls.FilterItem"
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"
d:DesignHeight="23" d:DesignWidth="590" xmlns:my="clr-namespace:AttorneyDashboard.Helpers" >
<StackPanel Orientation="Horizontal" x:Name="LayoutRoot">
<ComboBox Height="23" HorizontalAlignment="Left" Name="FieldName" VerticalAlignment="Top" Width="120" Margin="5,0,0,0" ItemsSource="{Binding Path=Parent.ColumnsList, ElementName=LayoutRoot}" SelectedItem="{Binding PropertyPath, Mode=TwoWay}" DisplayMemberPath="Header"/>
</StackPanel>
</UserControl>
You are not in control of name assigned to the user control, that belongs in the scope of the Xaml the uses your UserControl. If your code internally requires that the containing UserControl have a specific name then things are likely to break.

Resources