I have my little designer tool (my program).
On the left side I have TreeView and on the right site I have Accordion.
When I select a node I want to dynamically build Accordion Items based on Properties from DataContext of selected node.
Selecting nodes works fine, and when I use this sample code for testing it works also.
XAML code:
<layoutToolkit:Accordion x:Name="accPanel"
SelectionMode="ZeroOrMore"
SelectionSequence="Simultaneous">
<layoutToolkit:AccordionItem Header="Controller Info">
<StackPanel Orientation="Horizontal" DataContext="{Binding}">
<TextBlock Text="Content:" />
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</layoutToolkit:AccordionItem>
</layoutToolkit:Accordion>
C# code:
private void treeSceneNode_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue != e.OldValue)
{
if (e.NewValue is SceneNode)
{
accPanel.DataContext = e.NewValue; //e.NewValue is a class that contains Name property
}
}
}
But the problem occurs when I'm trying to achive this using DateTemplate and dynamically build AccordingItem, the Binding is not working:
<layoutToolkit:Accordion x:Name="accPanel"
SelectionMode="ZeroOrMore"
SelectionSequence="Simultaneous" />
and DataTemplate in my ResourceDictionary
<DataTemplate x:Key="dtSceneNodeContent">
<StackPanel Orientation="Horizontal" DataContext="{Binding}">
<TextBlock Text="Content:" />
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
and C# code:
private void treeSceneNode_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue != e.OldValue)
{
ResourceDictionary rd = new ResourceDictionary();
rd.Source = new Uri("/SilverGL.GUI;component/SilverGLDesignerResourceDictionary.xaml", UriKind.RelativeOrAbsolute);
if (e.NewValue is SceneNode)
{
accPanel.DataContext = e.NewValue;
AccordionItem accController = new AccordionItem();
accController.Header = "Controller Info";
accController.ContentTemplate = rd["dtSceneNodeContent"] as DataTemplate;
accPanel.Items.Add(accController);
}
else
{
// Other type of node
}
}
}
Are you missing this?
accController.Content = e.NewValue;
Also, I don't think you need to use DataContext="{Binding}"; the DataContext will inherit anyway.
Related
I want make TreeView with editable nodes. I googled this good, as I think, article:
http://www.codeproject.com/Articles/31592/Editable-TextBlock-in-WPF-for-In-place-Editing
But I have a problems. My TreeView formed dinamically, not statically as in the arcticle. Like that
<TreeView Name="_packageTreeView" Margin="5" ItemsSource="{Binding PackageExtendedList}">
<TreeView.InputBindings>
<KeyBinding Key="C" Command="{Binding *TestCommand*}" CommandParameter="{Binding}" />
</TreeView.InputBindings>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding PackageTreeItemChangeCommand}" CommandParameter="{Binding ElementName=_packageTreeView, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type MasterBuisnessLogic:RootDocPackage}" ItemsSource="{Binding Path=Childs}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition>
</ColumnDefinition>
<ColumnDefinition>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Source="/Resources/DocGroup.png"></Image>
<Etb:EditableTextBlock Margin="5,0,0,0" Grid.Column="1" Text="{Binding Path=Name}"></Etb:EditableTextBlock>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
PackageExtendedList - List of DocPackageExtended.
So, first question - how can I get TreeViewItem instance in TestCommand? Not instance DocPackageExtended class! I want to get instance selected TreeViewItem like in the article.
And second question - After I get instance TreeViewItem, how can I get EditableTextBlock from the TreeView item's DataTemplate.
added answer
I already tried it. Cause in MVVM ViewModel cannot has any link to View object like TreeView, I make handler in code-behind, like that
private void TreeViewItemSelected(object sender, RoutedEventArgs e)
{
// Already have TreeViewItem instance without of ItemContainerGenerator help
var tvi = e.OriginalSource as TreeViewItem;
if (tvi == null)
return;
var etb = VisualTreeLib.VisualTreeLib.GetVisualChild<EditableTextBlock>(tvi);
if (etb == null)
return;
// Do what I want
etb.IsEditable = true;
}
Unfortunately, this has no any affect :(
I also tried that approach, but also failed.
in DocPackageExtended type I define property
public bool IsEditable
{
get { return _isEditable; }
set
{
_isEditable = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsEditable"));
}
}
than change in XAML:
<Etb:EditableTextBlock Margin="5,0,0,0" Grid.Column="1" Text="{Binding Path=Name}" *IsEditable="{Binding Path=IsEditable}"*/>
and in ViewModel
private void TestCommandMethod(object obj)
{
var dpe = obj as DocPackageExtended;
if (dpe == null)
return;
dpe.IsEditable = true;
}
Doesn't work too :(
Any ideas?
This might help you.
private void Button_Click(object sender, RoutedEventArgs e)
{
TreeViewItem treeViewItemFound = GetItem(MyTreeview, MyTreeview.SelectedItem);
ContentPresenter header = treeViewItemFound.Template.FindName("PART_Header", treeViewItemFound) as ContentPresenter;
if (header != null)
{
TextBox myTextBox = (TextBox)header.ContentTemplate.FindName("MyTextBox", header);
}
}
public TreeViewItem GetItem(ItemsControl container, object itemToSelect)
{
foreach (object item in container.Items)
{
if (item == itemToSelect)
{
return (TreeViewItem)container.ItemContainerGenerator.ContainerFromItem(item);
}
else
{
ItemsControl itemContainer = (ItemsControl)container.ItemContainerGenerator.ContainerFromItem(item);
if (itemContainer.Items.Count > 0)
{
TreeViewItem treeViewItemFound = GetItem(itemContainer, itemToSelect);
if (treeViewItemFound != null)
return treeViewItemFound;
}
}
}
return null;
}
First question: Since it seems that you can select multiple entries, you need to filter all selected entries in TestCommand's executed method:
IEnumerable<DocPackageExtended> selectedEntries = PackageExtendedList.Where(d => d.IsSelected);
If multiple selection is disabled, you could bind the TreeView's selected item to a property in your VM and access this property in TestCommand's method.
Second question: You get the dataitem's container through var container = YourTreeViewInstance.ItemContainerGenerator.ContainerFromItem(dataInstance);. Now you have to go through this container with the help of the VisualTreeHelper until it finds a control of type EditableTextBlock. But I wouldn't do this in a ViewModel, rather in a helper-class or with the help of attached properties.
EDIT: You're binding the IsEditable property of the instances in the Childs property of your DocPackageExtended class to your EditableTextBox, but in your TestCommandMethod you're manipulating the IsEditableproperty of a DocPackageExtended instance directly. You could do the following:
private void TestCommandMethod(object obj)
{
var dpe = obj as DocPackageExtended;
if (dpe == null)
return;
dpe.IsEditable = true;
foreach (RootDocPackage rdp in dpe.Childs)
{
rdp.IsEditable = true;
}
}
Iam displaying messages in my WPF application
when a new message is added to the messages, i need to highlight it.so i want to dynamically get the text added to TextBlock
i have the xaml like this
<ItemsControl Name="DialogItemsControl" ItemsSource="{Binding Messages, Mode=OneWay}" Background="Transparent"
BorderBrush="Transparent" TargetUpdated="DialogItemsControl_TargetUpdated">
<ItemsControl.ItemTemplate><!-- For ever message -->
<DataTemplate>
<Grid Margin="0,0,0,20">
<ItemsControl Name="SubDialogItemsControl"
Foreground="{DynamicResource ButtonTextBrush}"
ItemsSource="{Binding Lines,NotifyOnTargetUpdated=True}"
Margin="0,0,0,12"
Grid.Column="0">
<ItemsControl.ItemTemplate><!-- For every line -->
<DataTemplate>
<TextBlock Name="DialogMessageText"
Text="{Binding NotifyOnTargetUpdated=True}"
VerticalAlignment="Top"
Margin="0,2,0,2"
TextTrimming="WordEllipsis"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and the code in the codebehind class is like this:
private void DialogItemsControl_TargetUpdated(object sender, System.Windows.Data.DataTransferEventArgs e)
{
ItemsControl itemControl = sender as ItemsControl;
ContentPresenter dp = itemControl.ItemContainerGenerator.ContainerFromItem(itemControl.Items.CurrentItem) as ContentPresenter;
// Finding textBlock from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = dp.ContentTemplate;
ItemsControl itc = (ItemsControl)myDataTemplate.FindName("SubDialogItemsControl", dp);
if (itc != null && itc.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
ContentPresenter cp = itc.ItemContainerGenerator.ContainerFromIndex(0) as ContentPresenter;
DataTemplate dt = cp.ContentTemplate;
TextBlock tb = dt.LoadContent() as TextBlock;
tb.TargetUpdated += new EventHandler<System.Windows.Data.DataTransferEventArgs>(myTextBlock_TargetUpdated);
}
}
void myTextBlock_TargetUpdated(object sender, System.Windows.Data.DataTransferEventArgs e)
{
TextBlock tb = sender as TextBlock;
//When i access the text property of tb, its showing null, how to get the text
}
When i access the text property of textblock in the target updated event of textblock, its showing null, how to read the text.
Thanks in advance
You tackle the problem from the wrong angle (and probably add a memory leak in the process since I don't see you unsubscribing to the event).
You need to create a Custom TextBlock, overriding the metadata of the Text property so that it changes the Background for a few seconds when the text string changes (through PropertyChangedCallback).
And then use that custom TextBlock in the DataTemplate of your ItemsControl.
EDIT - I thought other people could need this feature so here is a working example:
public class CustomTextBlock : TextBlock
{
static CustomTextBlock()
{
TextProperty.OverrideMetadata(typeof(CustomTextBlock), new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(
(dpo, dpce) =>
{
//Flash the background to yellow for 2 seconds
var myTxtblk = dpo as CustomTextBlock;
if (myTxtblk != null)
{
myTxtblk.Background = Brushes.Yellow;
Task.Factory.StartNew(
() =>
{
Thread.Sleep(2000);
Application.Current.Dispatcher.Invoke(
new Action(() =>
{
myTxtblk.Background = Brushes.Transparent;
}));
});
}
})));
}
}
Then you need to declare the right xmlns namespace in your XAML view, and you use it like a regular TextBlock:
<local:CustomTextBlock Text="{Binding MyDynamicText}"/>
It will flash yellow when MyDynamicText is modified (provided it raises PropertyChanged).
I ve a list from sharepoint and i collect from this list an hyperlink.
As i want my textbox to be like an hyperlink I ve added an event on mousedown to open this hyperlink, My concern is how to collect this hyperlink in the codebehind with the sender.
For the moment I've just hide this hyperlink in the tooltip maybe i can manage this differently any suggestion will be grantly appreciated.
My point so far, i don't know how to get this tooltip in the code behind.
Thanks
My XAML Code :
<ListBox Name="ListboxTips" ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=Picture}" Height="20"></Image>
<TextBlock MouseDown="TextBlock_MouseDown_URL" TextDecorations="Underline"
Margin="10,10,20,10" Width="160" TextWrapping="Wrap"
Text="{Binding Path=TitleTip}"
ToolTip="{Binding Path=URL}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My code behind :
foreach (SPSClient.ListItem item in TipsList)
{
var tips = new Tips();
tips.TitleTip = item.FieldValues.Values.ElementAt(1).ToString();
tips.App = item.FieldValues.Values.ElementAt(4).ToString();
// get the Hyperlink field URL value
tips.URL = ((FieldUrlValue)(item["LinkDoc"])).Url.ToString();
//should collect the description of the url
//tips.URLdesc = ((FieldUrlValue)(item["LinkDoc"])).Description.ToString();
tips.Picture = item.FieldValues.Values.ElementAt(4).ToString();
colTips.Add(tips);
}
ListboxTips.DataContext = colTips;
....
private void TextBlock_MouseDown_URL(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//string test = (ToolTip)(sender as Control).ToString();
System.Diagnostics.Process.Start("http://www.link.com");
//System.Diagnostics.Process.Start(test);
}
Thanks a lot,
You can just access the property directly. It is not elegant, but will work!
private void TextBlock_MouseDown_URL(object sender, MouseButtonEventArgs e)
{
TextBlock txtBlock = sender as TexBlock;
// just access the property
string url = txtBlock.ToolTip as string;
}
A more elegant approach might be to use a Button, Hyperlink or something that exposes a Command, so that you can bind the 'click' action to a command on your view model that performs the action you wish to execute.
usually you stick any data you want to trespass somewhere to Tag attribute.
<TextBlock .. Tag="{Binding Path=URL}" />
This is easily retrievable as a public property:
private void TextBlock_MouseDown_URL(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var tb = sender as TextBlock;
if(tb != null)
{
var neededUrl = tb.Tag;
}
}
When I make selection in ComboBox, and then type some text in TextBox, I want to have visible AutoSuggestion list of ID or FirstName or LastName (based on ComboBox Selection) that contains typed string in TextBox. Like this, now it works only for FirstName.
I have problem to somehow set dynamically binding for TextBlock.
Please Help.
Thanks in advance! Marina
I have ComboBox:
<ComboBox Height="23" Name="cbAttrib" Width="120" Margin="0,8,0,0">
<ComboBoxItem>ID</ComboBoxItem>
<ComboBoxItem>FirstName</ComboBoxItem>
<ComboBoxItem>LastName</ComboBoxItem>
</ComboBox>
I have TextBox:
<TextBox Name="txtSearch" TextChanged="txtAutoSuggestName_TextChanged"/>
And this ListBox:
<ListBox Name="listBoxSuggestion" Visibility="Hidden" SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding FirstName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and in code I have this methods:
private void txtAutoSuggestName_TextChanged(object sender, TextChangedEventArgs e)
{
listBoxSuggestion.Items.Clear();
if (txtSearch.Text != "")
{
ComboBoxItem cb = (ComboBoxItem)cbAttrib.SelectedItem;
Collection<Person> namelist = proxy.PersonSearch(txtSearch.Text, cb.Content.ToString());
if (namelist.Count > 0)
{
listBoxSuggestion.Visibility = Visibility.Visible;
foreach (var obj in namelist)
{
listBoxSuggestion.Items.Add(obj);
}
}
}
else
{
listBoxSuggestion.Visibility = Visibility.Hidden;
}
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
txtSearch.Text = (e.AddedItems[0] as Person).FirstName.ToString();
listBoxSuggestion.Visibility = System.Windows.Visibility.Hidden;
}
}
You are not binding the Text so nothing will display
You just bind the DataContext, which does nothing if there are no additional bindings which will be relative to it. Just swap that (or add Text="{Binding}" which will bind to the DataContext which is the FirstName) and if your logic is correct it should work.
(Instead of clearing and adding to Items you should just set the ItemsSource instead. listBoxSuggestion.ItemsSource = namelist;)
Edit: To make the binding work for different suggestions change the binding path to Value and make the ItemsSource a collection of some simple objects with a Value property (e.g. use LINQ and anonymous objects).
say I have a ListView with an ItemControl. And a Details part that shows the selected Item from the ListView. Both are in the same xaml page. I tried everything to accomplish it, but what do I miss?
<!-- // List -->
<ItemsControl ItemsSource="{Binding Path=Model, ElementName=SomeListViewControl, Mode=Default}" SnapsToDevicePixels="True" Focusable="False" IsTabStop="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<SomeListView:SomeListItemControl x:Name=listItem/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- // Details -->
<Label Content="Begindatum" FontSize="16" VerticalAlignment="Center" Grid.Row="1" Margin="2,0,0,0"/>
<TextBox x:Name="Begindatum" Grid.Column="1" Grid.Row="1" Text="{Binding Path=BeginDate, ElementName=listItem,Converter={StaticResource DateTimeConverter}, ConverterParameter=dd-MM-yyyy}" IsEnabled="False" Style="{DynamicResource TextBoxStyle}" MaxLength="30"/>
public event EventHandler<DataEventArgs<SomeEntity>> OnOpenSomething;
public ObservableCollection<SomeEntity> Model
{
get { return (ObservableCollection<SomeEntity>)GetValue(ModelProperty); }
set
{
Model.CollectionChanged -= new NotifyCollectionChangedEventHandler(Model_CollectionChanged);
SetValue(ModelProperty, value);
Model.CollectionChanged += new NotifyCollectionChangedEventHandler(Model_CollectionChanged);
UpdateVisualState();
}
}
public static readonly DependencyProperty ModelProperty = DependencyProperty.Register("Model", typeof(ObservableCollection<SomeEntity>), typeof(SomeListView), new UIPropertyMetadata(new ObservableCollection<SomeEntity>(), new PropertyChangedCallback(ChangedModel)));
private static void ChangedModel(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
SomeListView someListView = source as SomeListView;
if (someListView.Model == null)
{
return;
}
CollectionView cv = (CollectionView)CollectionViewSource.GetDefaultView(someListView.Model);
}
private void Model_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (Model == null)
{
return;
}
}
Do not use an ItemsControl - ItemsControl does not have a SelectedItem property - and therefore you cannot determine which one is selected.
Use a ListBox instead and then in the detail section make a binding like so: ... DataContext="{Binding SelectedItem,ElementName=ListboxName}" ... where ListboxName is the Name property of the ListBox you use.