I need to create a component with Dependency Property, I don´t know how to bind the data from another user control, can someone explain it to me? I don´t think i need to create a viewmodel for the component.
Here´s my code:
Here I call the component
<DockPanel Grid.Row="1">
<ctrls:CustomComboBox Collection="{Binding Path=TestingCollection}" SelectedCollectionItem="{Binding Path=SelectedItem}"
CreateCommand="" DeleteCommand="" UpdateCommand=""/>
</DockPanel>
And Here it´s my component
<ComboBox Grid.Column="0" SelectedValue="{Binding SelectedCollectionItem,ElementName=CustomComboBoxControl, Mode=TwoWay}"
ItemsSource="{Binding Collection}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
/>
Here it is my xaml.cs
public static readonly DependencyProperty CollectionProperty =
DependencyProperty.Register("Collection", typeof(ObservableCollection<object>), typeof(CustomComboBox));
public ObservableCollection<object> Collection
{
get { return (ObservableCollection<object>)GetValue(CollectionProperty); }
set { SetValue(CollectionProperty, value); }
}
public static readonly DependencyProperty SelectedCollectionItemProperty =
DependencyProperty.Register("SelectedCollectionItem", typeof(object), typeof(CustomComboBox));
public object SelectedCollectionItem
{
get { return (object)GetValue(SelectedCollectionItemProperty); }
set { SetValue(SelectedCollectionItemProperty, value); }
}
EDIT:
I tryed creating a VM and setting the data context, and works, but dependency properties seem pointless
EDIT 2:
I succeded, i delete my VM i was lacking and ElementName in ItemSource
I succeded, i delete my VM i was lacking and ElementName in ItemSource
Related
Got a user control defined as follows:
public partial class EveBlueprintsGrid : UserControl
{
public static DependencyProperty BlueprintListProperty = DependencyProperty.Register(
"EveBlueprints",
typeof(EveBlueprintList),
typeof(EveBlueprintsGrid));
public EveBlueprintList EveBlueprints
{
get { return (EveBlueprintList)GetValue(BlueprintListProperty); }
set { SetValue(BlueprintListProperty, value); }
}
with XAML:
<DataGrid x:Name="BlueprintsDataGrid" Grid.Row="0" IsReadOnly="True" ItemsSource="{Binding Path=EveBlueprints.Blueprints, ElementName=uc}" AutoGenerateColumns="False">
With the calling XAML:
<Blueprints:EveBlueprintsGrid Grid.Column="1" EveBlueprints="{Binding Blueprints}"/>
I am getting the "A Binding can only be set on a DependencyProperty of a DependencyObject." When I look at the calling XAML.
EveBlueprints contains property Blueprints which is an ObservableCollection.
What's interesting is I have an almost exact copy of this on another object which isn't giving me the errors I'm seeing.
What am I missing here?
I'm having trouble binding to a custom dependencyproperty on a usercontrol to my MVVM ViewModel. My user control is correctly working when i use it directly on my view:
<local:CustomControl Mode="{Binding Mode, Mode=TwoWay}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0">
<Button x:Name="InfoBox1" Content="Test1" />
<Button x:Name="InfoBox2" Content="Test2" />
</local:CustomControl>
But using it as an itemspaneltemplate the binding is not working:
<ItemsControl Grid.Row="0" ItemsSource="{Binding Equipment}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:CustomControl Mode="{Binding Mode, Mode=TwoWay}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've tried using a RelativeSource and finding the itemscontrol/view and setting the path to either Mode or DataContext.Mode but I just can't get the binding to work.
Mode is defined as:
public static readonly DependencyProperty ModeProperty;
public Modes Mode
{
get { return (Modes)this.GetValue(ModeProperty); }
set { this.SetValue(ModeProperty, value); }
}
and registered in the constructor of the custom control:
public CustomControl()
{
Mode = Modes.Default;
}
static CustomControl()
{
ModeProperty = DependencyProperty.Register("Mode", typeof(Modes), typeof(CustomControl), new FrameworkPropertyMetadata(Mode.Default, OnModeChanged));
}
private static void OnModeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
CustomControl ctrl= o as CustomControl ;
if (ctrl== null) return;
Modes mode = (Modes)e.NewValue;
ctrl.Mode = mode;
}
Do I need to use a workaround to get the control working as the panel template or am I just messing up the binding too bad?
----Edit
The viewmodel part:
private Modes _mode= Modes.Default;
public Modes Mode
{
get { return _mode; }
set { _mode= value; NotifyPropertyChanged(); }
}
private ObservableCollection<EquipmentViewModel> _equipment;
public ObservableCollection<EquipmentViewModel> Equipment
{
get { return _equipment; }
set { _equipment = value; NotifyPropertyChanged(); }
}
----Edit2:
I've investigated further and I'm more complexed. I've added the following to both the ItemsPanelTemplate's control and the one directly in the grid.
Visibility="{Binding Visible, Converter={StaticResource visibilityConverter}}"
Altering this Visible boolean works in both cases. So it appears to only be an issue with the custom DependencyProperty.
Inspecting the visual tree the DataContext of the control as ItemsPanelTemplate is also correct.
What could make the dependency property work properly when used straight and not when used as an itemspaneltemplate ?
Found what was causing the strange conflicting behavior.
I was setting the property to a certain value in the normal ctor
public CustomControl()
{
Mode = Modes.Default;
}
This was appearently causing a conflict when using the control as an Itemspaneltemplate. Removing this made the binding work as expected.
I guess the difference in behavior has something to do with the calls to the constructor at different times ?
I have a Custom Controller that works fine like this:
<Controller:DLBox ID="{Binding SHVM.Selected.ID}" />
I mean binding ID to a property in ViewModel. But when I want to bind it like this:
<ScrollViewer DataContext="{Binding SHVM.Selected}">
<Controller:DLBox ID="{Binding ID}" />
</ScrollViewer>
Binding to DataContext of parent, It doesn't work at all. I have some other Custom Controllers and they do fine, But I don't know what the hell is this one's problem!
This is the controller:
public partial class DLBox : UserControl
{
public static DependencyProperty IDProperty =
DependencyProperty.Register("ID", typeof(int), typeof(DLBox),
new FrameworkPropertyMetadata(0,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, e) => (o as DLBox).IDPropertyChanged((int)e.NewValue)));
public int ID { get; set; }
private void IDPropertyChanged(int e)
{
ID = e;
}
}
Could someone please tell my what's wrong? Because I'm debugging for 6 hours straight and didn't find anything! Thanks a lot.
UPDATE:
That worked with just adding:
<... DataContext={Binding} .../>
And I don't know why!
Now the real problem is that I want to use this inside 2 ItemsControls and even with DataContext Still doesn't work.
(Just for clarification, I have 2 Lists inside each other. Think about the first one like 10 schools First List, and inside each school there is some studens Second List)
<ItemsControl ItemsSource="{Binding Source={StaticResource Locator}, Path=SHVM.Extra.Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl DataContext="{Binding}" ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Controller:DLBox DataContext="{Binding}" ID="{Binding ID}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
<ItemsControl.ItemTemplate>
</ItemsControl>
UPDATE 2:
ID is just a TextBlock In a UserControl. There is nothing that I can show here!
All I did, Just set the Text of TextBlock inside the PropertyCallBack (Didn't use MVVM inside my controllers):
<TextBlock x:Name="txtIDValue"/>
And inside CodeBehind:
private void IDPropertyChanged(string e)
{
ID= e;
txtIDValue.Text = e;
}
There is nothing relevant to this problem, And that's why I couldn't figure it out!
Appreciate any help.
ANSWER:
After 12 hours working on it, I find out that It was an idiotic mistake! I don't know why and when I set DataContext in my Controller's XAML!
Anyway, Thanks.
Your dependency property declaration is wrong, because the getter and setter of the CLR wrapper must call the GetValue and SetValue methods respectively. Besides that, your PropertyChangedCallback is redundant. There is no need to set the property again in a callback that is called when the property value has just been set.
The declaration should look like this:
public static readonly DependencyProperty IDProperty = DependencyProperty.Register(
"ID", typeof(int), typeof(DLBox),
new FrameworkPropertyMetadata(
0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public int ID
{
get { return (int)GetValue(IDProperty); }
set { SetValue(IDProperty, value); }
}
I have the following ListBox with the ContentControl as DataTemplate:
<ListBox x:Name="lstActionConfigs" ItemsSource="{Binding Path=AllActionConfigList}" SelectedItem="{Binding Path=ListSelectedItem, Mode=TwoWay}" HorizontalContentAlignment="Stretch" Grid.Row="3" Margin="0,0,0,5">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type helper:ItemDetails}">
<ContentControl Template="{StaticResource ResourceKey=actionDetailsListItemTemplate}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<i:Interaction.Behaviors>
<behaviours:BringIntoViewBehaviour CustomIsSelected="{Binding Path=IsSelected, Mode=TwoWay}"/>
</i:Interaction.Behaviors>
</ContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Each bounded instance has 'IsSelected' property which notify the UI on changes via INotifyPropertyChanged:
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
notify("IsSelected");
}
}
I built a custom behavior that brings into view the elements that changed it's IsSelectedProperty to true, as the follows:
public class BringIntoViewBehaviour : Behavior<FrameworkElement>
{
public bool CustomIsSelected
{
get { return (bool)GetValue(CustomIsSelectedProperty); }
set { SetValue(CustomIsSelectedProperty, value); }
}
public static readonly DependencyProperty CustomIsSelectedProperty =
DependencyProperty.Register("CustomIsSelected", typeof(bool), typeof(BringIntoViewBehaviour), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(customIsSelectedPropertyChanged_Callback)));
private static void customIsSelectedPropertyChanged_Callback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
BringIntoViewBehaviour thisControl = o as BringIntoViewBehaviour;
if (thisControl == null)
return;
bringIntoView(thisControl);
}
}
This item is not presented this moment on the UI as it located at the bottom of the list (there is a scroll bar).
I updated the IsSelected property with true value.
However, the customIsSelectedPropertyChanged_Callback method should be executed as we updated it's bounded property.
But, in practice, this method is invoked only when this item is presented on UI when moving the scroll bar down to it.
The reason most likely is UI virtualization. ListBox items host is by default VirtualizingStackPanel. It will not generate items which are out of view now, so when you set IsSelected on your model, your DataTemplate together with your behaviour are not created yet. Only when you scroll down, control is created together with behaviour from data template, and after it is bound CustomIsSelectedProperty is set to true, so your callback is called.
To verify this assumption you can disable UI virtualization for your ListBox and see if that resolves the problem.
I'm trying to bind a Dependency Property from my UserControl to my MainViewModel.
This is how the DependencyProperty looks like:
public static DependencyProperty ItemHasChangesProperty = DependencyProperty.Register("ItemHasChanges",
typeof(bool),
typeof(MyUserControl),
new PropertyMetadata(null));
public bool ItemHasChanges
{
get { return (bool)GetValue(ItemHasChangesProperty); }
set { SetValue(ItemHasChangesProperty, value); }
}
My XAML:
<local:MyUserControl ItemHasChanges="{Binding Path=Changes}" Grid.Row="4" />
Now when debugging and checking the Set-Accessor of bool Changes, I see that it never gets accessed when I set in the UserControl ItemHasChanges = true;
Any idea what I'm doing wrong here?
Thanks!
Cheers
Got it.. I had to change
<local:MyUserControl ItemHasChanges="{Binding Path=Changes}" Grid.Row="4" />
to
<local:MyUserControl ItemHasChanges="{Binding Path=Changes, Mode=OneWayToSource}" Grid.Row="4" />
Took me about 3h to figure it out.. haha :-)
Cheers
Are you setting ItemHasChanges on the control directly (as in, not by updating the binding source)? If so, that will remove the binding.