In my TabControl using WPF,C#.I entering Text to ListBox in one TabItem from the click event in TabControl. But the ListBox does not display the Text. When I debug, I can find that the ListBox has count:1. Here is the code:
namespace Tabcontrol
{
public partial class PresetTab : UserControl //3rd Tabitem ,preset.xaml.cs
{
public PresetTab()
{
InitializeComponent();
}
public void AddPresetmenu(string pMenu)
{
menubox.Items.Add(pMenu); //menubox is listbox
}
}
}
namespace Tabcontrol
{
public partial class ToolBar : UserControl
{
PresetTab tab = new PresetTab();
public ToolBar()
{
InitializeComponent();
}
public void Click(object sender, MouseButtonEventArgs e)
{
Add("TAB MENU");
}
public void Add(string menu)
{
tab.AddPresetmenu(menu); //Im calling from tabcontrol,toolbar.xaml.cs
}
}
}
It would be easier to say for sure if you would have added your XAML code as well, but it seems to me that your adding the strings directly to the Items property and aren't applying a DataTemplate specifying how to display the strings. So either apply a DataTemplate which turns the string into a UIElement, e.g. a TextBlock, or try to add the TextBlocks in your code instead of strings.
public void AddPresetmenu(string pMenu)
{
TextBlock tb= new TextBlock();
tb.Text = pMenu;
menubox.Items.Add(tb);
}
Hope this helps, if not please include your XAML, this will make it easier to spot the problem.
Related
I have a listbox that is bound to a list of custom objects. I can get the listbox items to display correctly using the ListBox.ItemTemplate in xaml. The custom objects for the listbox are all of the same base class outlined below.
public class HomeViewMenuItem : UIElement
{
private Uri _uri;
private IRegionManager _manager;
public HomeViewMenuItem(string text, Uri uri, IRegionManager manager)
{
this.PreviewMouseDown += HomeViewMenuItem_PreviewMouseDown;
this.PreviewKeyDown += HomeViewMenuItem_PreviewKeyDown;
_manager = manager;
Text = text;
_uri = uri;
ClickCommand = new DelegateCommand(this.Click, this.CanClick);
}
void HomeViewMenuItem_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter)
{
e.Handled = true;
this.ClickCommand.Execute();
}
}
void HomeViewMenuItem_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
this.ClickCommand.Execute();
}
private void Click()
{
_manager.Regions[RegionNames.MainRegion].RequestNavigate(_uri);
}
private bool CanClick()
{
return true;
}
public DelegateCommand ClickCommand { get; set; }
public string Text { get; set; }
}
The problem I am having is the HomeViewMenuItem_PreviewKeyDown method is not getting called. I believe this is because the method is getting called on the ListBoxItem itself first and getting handled there. I was able to verify this by obtaining a reference to the ListBoxItem object through listBox.ItemContainerGenerator.ContainerFromIndex(0) after the ItemContainerGenerator status changes to ContainersGenerated and adding an event handler there. This event handler correctly fired. Normally this would be an ok solution on a small project but I plan on having more listboxes with the same sort of functionality and would like to have a simpler/better solution. Is there a way that I can get my base class previewkeydown method to work?
The only solution I could think of is to have the base class inherit from ListBoxItem instead of UIElement then get the ListBox to create my items instead of ListBoxItems. But I dont think that is really possible without creating my own ListBox implementation.
You seem to be somewhat confused. In WPF, we create data items and declare DataTemplates to define what those items should look like in the UI. Our data items do not extend UI classes. If you have to handle the PreviewKeyDown event, then attach a handler to the UI element in the DataTemplate instead:
<DataTemplate>
<Grid PreviewKeyDown="HomeViewMenuItem_PreviewKeyDown">
...
</Grid>
</DataTemplate>
I'm trying to create a custom usercontrol that acts like a button, but i want it to use different images for each state (Normal, Hover, Pressed). The user control contains an Image control to show the image. I want to change the Source of the Image control at-runtime, so when the OnMouseEnter event triggers, i would change my image source to the HoverChange (ImageSource) property.
So i tried to add 3 ImageSource properties (NormalState, HoverState and PressedState) to the usercontrol so i can change the images when needed. (Coming from WinForms) But the problem is that the Properties aren't set in code (WinForms behaviour), so i can't assign them to my image. But when i use the usercontrol in my program i can set them via the property panel, but i can't use them in code (they stay NULL).
Here is some (pseudo) code of what i'm trying to reach:
public partial class ThreeStateButton : UserControl
{
public enum ButtonState
{
Normal,
Hover,
Pressed
}
public ImageSource NormalState { get; set; }
public ImageSource HoverState { get; set; }
public ImageSource PressState { get; set; }
public ThreeStateButton()
{
InitializeComponent();
SetState(ButtonState.Normal);
}
public void SetState(ButtonState state)
{
switch (state)
{
case ButtonState.Normal:
imgButton.Source = NormalState;
break;
case ButtonState.Hover:
imgButton.Source = HoverState;
break;
case ButtonState.Pressed:
imgButton.Source = PressState;
break;
}
}
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
SetState(ButtonState.Hover);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
SetState(ButtonState.Normal);
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
SetState(ButtonState.Pressed);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
SetState(ButtonState.Hover);
}
}
The main problem is that the ImageSource properties aren't set after initialization (and yes there are set through the property panel in the editor), i know the way of working is a bit different in WPF but how could i get this working the way i try?
Thanks
I did it on a more WPF-style way.
I create 3 image source properties and for each of them i created a DependencyProperty. I also added 3 Image controls in my xaml code and binded those imagesource properties using their registered name as DependencyProperty to the imagesource property of my image controls. Then it's just a matter of hiding the images or setting them visible on the correct state.
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void ToggleButton_Checked(object sender, RoutedEventArgs e)
{
switch ((sender as Button).Content.ToString())
{
case "UserControl 1":
AddItemToContainer(new UserControl1());
break;
case "UserControl 2":
AddItemToContainer(new UserControl2());
break;
case "UserControl 3":
AddItemToContainer(new UserControl3());
break;
default:
break;
}
}
void AddItemToContainer(UIElement _myElement)
{
Grid.SetColumn(_myElement, 1);
HostContainer.Children.Add(_myElement);
}
}
}
}
With this I can open a new userControl in myMainwindow
Let’s say something like adding child to myMainWinodw,Now I’m trying to click on a button from my userControl so I open another userControl that take the place of the first one
I explain:
I have the mainWindows it has 3 button first one to open the first UserControl the second one to open the second userControl and the third to open the last UserControl,imagine that I opened the first UserControl let’s call it UC1,
In the UC1 I have a button to open the second userControl (let’s call it UC2) I like that when I clik the button from the UC1 the UC2 is opened and take the place of the UC1 (of course the UC2 is still a child of myMainWinodw)
I have alredy try to call the AddItemToContainer methode from other methode but nothing is happened
Any suggestion please
This approach may help:
Call the container control and modify it's Children.This example assumes that the container control is a Grid.
C# Code for Window1
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
HostContainer.Children.Add(new UserControl1(HostContainer));
}
}
C# Code for UserControl1
public partial class UserControl1 : UserControl
{
Grid _hostContainer;
public UserControl1(Grid HostContainer)
{
InitializeComponent();
_hostContainer = HostContainer;
}
private void ToggleButton_Checked(object sender, RoutedEventArgs e)
{
UserControl2 UC2 = new UserControl2();
_hostContainer.Children.Add(UC2);
}
}
Yes that helps but it does not realy take the place of the UC1 It just puch it a litel bit and take a litelle space of the mainWindow here is some snaps with explanation if you like http://startou.com/file/630-9411954b18.html
I have a listbox defined in XAML as:
<ListBox x:Name="directoryList"
MinHeight="100"
Grid.Row="0"
ItemsSource="{Binding Path=SelectedDirectories}"/>
The SelectedDirectories is a property on the lists DataContext of type List<DirectoryInfo>
The class which is the datacontext for the listbox implements INotifyPropertyChanged. When the collection changes the items are added successfully to the list however the display does not update until I force the listbox to redraw by resizing it.
Any ideas why?
EDIT: INotifyPropertyChanged implementation
public class FileScannerPresenter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private FileScanner _FileScanner;
public FileScannerPresenter()
{
this._FileScanner = new FileScanner();
}
public List<DirectoryInfo> SelectedDirectories
{
get
{
return _FileScanner.Directories;
}
}
public void AddDirectory(string path)
{
this._FileScanner.AddDirectory(path);
OnPropertyChanged("SelectedDirectories");
}
public void OnPropertyChanged(string property)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Try
ObservableCollection<DirectoryInfo>
instead - you're triggering a refresh of the entire ListBox for no reason, and you don't need to make your hosting class implement INotifyPropertyChanged - it could easily just be a property of the window. The key is to never set the property to a new instance. So:
class SomeWindow : Window {
public ObservableCollection<DirectoryInfo> SelectedDirectories {get; private set;}
SomeWindow() { SelectedDirectories = new ObservableCollection<DirectoryInfo>(); }
public void AddDirectory(string path) {
SelectedDirectories.Add(new DirectoryInfo(path));
}
}
If you end up using that FileScanner class, you need to implement INotifyCollectionChanged instead - that way, the ListBox knows what to add/remove dynamically.
(See Update below). WPF seems to be working alright. I put your code into a new project. The listbox updates whenever I click the button to invoke AddDirectory. You should not need any more code changes.
The problem seems to be something else.. Are there multiple threads in your UI?
I didnt have the FileScanner type. So I created a dummy as follows.
public class FileScanner
{
string _path;
public FileScanner()
{ _path = #"c:\"; }
public List<DirectoryInfo> Directories
{
get
{
return Directory.GetDirectories(_path).Select(path => new DirectoryInfo(path)).ToList();
}
}
internal void AddDirectory(string path)
{ _path = path; }
}
No changes to your FileScannerPresenter class. Or your listbox XAML. I created a Window with a DockPanel containing your listbox, a textbox and a button.
Update: Paul Betts is right. It works because I return a new list each time from the Bound property. Data binding with lists always messes me up.
With more tinkering, the easy way to do this is:
Make FileScanner#Directories return an ObservableCollection<DirectoryInfo> (which implements INotifyCollectionChanged for you). Change all signatures all the way up to return this type instead of a List<DirectoryInfo>
FileScanner and FileScannerPresenter themselves do not have to implement any INotifyXXX interface.
// in FileScanner class def
public ObservableCollection<DirectoryInfo> Directories
{
get
{ return _DirList; }
}
internal void AddDirectory(string path)
{
_path = path;
//var newItems = Directory.GetDirectories(_path).Select(thePath => new DirectoryInfo(thePath)).ToList();
//_DirList.Concat( newItems ); -- doesn't work for some reason.
foreach (var info in Directory.GetDirectories(_path).Select(thePath => new DirectoryInfo(thePath)).ToList())
{
_DirList.Add(info);
}
}
I would like to have my WPF Listbox, which is databound, generate subclassed ListboxItems instead of the regular ListboxItems. In this case, a DataTemplate is not sufficient because I need some custom properties for the subclassed ListBoxItems.
Is there a way to have the ListBox generated mySubClassedListBoxItem items for the bound data?
Thanks,
Bart
You need to create your own subclass of ListBox so you can override the method which creates the container, e.g.
public class MyListBox : ListBox
{
public MyListBox()
{
// Should get the default style & template since styles are not inherited
Style = FindResource(typeof(ListBox)) as Style;
}
protected override DependencyObject GetContainerForItemOverride()
{
var container = new MyListBoxItem();
return container;
}
}
public class MyListBoxItem : ListBoxItem
{
public MyListBoxItem()
{
Style = FindResource(typeof(ListBoxItem)) as Style;
// To easily see that these are custom ListBoxItems:
// TextElement.SetForeground(this, Brushes.Red);
}
// ...
}