2

I am trying to solve this issue for so many hours:

I have user custom control of grid named NewMazeGrid and I want to use it as a control in MainWindow. MainWindow contains MazeViewModel(mazeVM member).

I'm trying to set the values of the grid, when the property MazeViewModel:MySingleplay changes. (I'm using the INotifyPropertyChanged for it, and it works perfectly fine. I guess, the problem is in the final binding) The code:

This is the property MazeViewModel:MySingleplay getter:

public string MySingleplay
        {
            get
            {
                if (myModel.MySingleplay == null)
                {
                    return "";
                } else
                {
                    return myModel.MySingleplay.ToString();//works perfect
                }
            }
        }

this is the NewMazeGrid.xaml.cs:

namespace VisualClient.View.controls
{
    public partial class NewMazeGrid : UserControl
    {
        private MazePresentation myMaze;
        private string order; //dont really use it

        //Register Dependency Property

        public static readonly DependencyProperty orderDependency =
            DependencyProperty.Register("Order", typeof(string), typeof(NewMazeGrid));

        public NewMazeGrid()
        {
            myMaze = new MazePresentation();
            InitializeComponent();
            DataContext = this;
            lst.ItemsSource = myMaze.MazePuzzleLists;
        }

        public string Order
        {
            get
            {
                return (string)GetValue(orderDependency);
            }
            set
            {
                SetValue(orderDependency, value);
                myMaze.setPresentation(value); //(parsing string into matrix)
            }
        }
    }
}

this is the MainWindow.xaml.cs:

public partial class MainWindow : Window
    {
        private MazeViewModel mazeVM;

        public MainWindow()
        {
            InitializeComponent();

            mazeVM = new MazeViewModel(new ClientMazeModel(new TCPClientConnection()));
            DataContext = mazeVM;

            mazeVM.connectToServer();
        }

        private void bu_Click(object sender, RoutedEventArgs e)
        {
            bool isC = mazeVM.isConnected();
            mazeVM.openSingleplayGame("NewMaze");//works perfect
        }

this is the MainWindow.xaml:

<Window x:Class="VisualClient.View.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:Controls ="clr-namespace:VisualClient.View.controls"
        xmlns:vm ="clr-namespace:VisualClient.ViewModel"
        xmlns:local="clr-namespace:VisualClient.View"

        mc:Ignorable="d"
        Title="Main Window" Height="350" Width="525" MinWidth="900" MinHeight="600">
    <WrapPanel >
        <Button Name ="bu" Content="Click_Me" Click="bu_Click"/>
        <Grid Name="myGrid">
            <Controls:NewMazeGrid Order="{Binding MySingleplay, UpdateSourceTrigger=PropertyChanged}"/>
        </Grid>
    </WrapPanel>
</Window>

I get this error on the binding line: Value cannot be null.

To sum: It initialize fine the window in the ctor, but when the property changes it does not get into the Order property setter. therefor my grid never changes.

What should be the right syntax for binding in this case? how do I bind it to the right property?

Folders hierarchy explorer

OfKo
  • 23
  • 1
  • 3
  • Not sure about the binding error but for why your setter isn't being called look [here](http://stackoverflow.com/a/3836123/1454658) – James Durda May 06 '16 at 02:16
  • thx! but still doesn't even get into the MySingleplay property getter – OfKo May 06 '16 at 12:11

1 Answers1

0

WPF may not call the CLR wrapper of a dependency property, but just directly call the GetValue and SetValue methods of the underlying DependencyObject. This is why there should not be any logic except the GetValue and SetValue calls.

This is explained in XAML Loading and Dependency Properties:

Because the current WPF implementation of the XAML processor behavior for property setting bypasses the wrappers entirely, you should not put any additional logic into the set definitions of the wrapper for your custom dependency property. If you put such logic in the set definition, then the logic will not be executed when the property is set in XAML rather than in code.

Similarly, other aspects of the XAML processor that obtain property values from XAML processing also use GetValue rather than using the wrapper. Therefore, you should also avoid any additional implementation in the get definition beyond the GetValue call.


To get notified about property value changes, you can register a PropertyChangedCallback by property metadata. Note also that there is a naming convention for DependencyProperty fields. Yours should be called OrderProperty:

public static readonly DependencyProperty OrderProperty =
    DependencyProperty.Register(
        "Order", typeof(string), typeof(NewMazeGrid),
        new PropertyMetadata(OnOrderChanged));

public string Order
{
    get { return (string)GetValue(OrderProperty); }
    set { SetValue(OrderProperty, value); }
}         

private static void OnOrderChanged(
    DependencyObject obj, DependencyPropertyChangedEventArgs e) 
{
    ((NewMazeGrid)obj).myMaze.setPresentation((string)e.NewValue);
}

Besides that, you must not set

DataContext = this;

in the constructor of NewMazeGrid. This effectively prevents inheriting the DataContext from the parent window, so that {Binding MySingleplay} won't work. Except under special circumstances you should never explicitly set a UserControl's DataContext.

So, remove the DataContext assignment from the constructor:

public NewMazeGrid()
{
    myMaze = new MazePresentation();
    InitializeComponent();
    lst.ItemsSource = myMaze.MazePuzzleLists;
}

That said, there is also no need to set UpdateSourceTrigger=PropertyChanged on a one-way binding. It only has an effect in two-way (or one-way-to-source) bindings:

<Controls:NewMazeGrid Order="{Binding MySingleplay}"/>
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Thank you for your comment! I added the _OnOrderChanged_ function but still, it doesn't get into the _MySingleplay getter_ when it notifies change. – OfKo May 06 '16 at 11:32
  • THANKS! now it gets into the properties and changes the lists of myGrid. but still the changes does not appear on screen. I wonder if removing `DataContext = this` can be the problem? I used this code for showing the lists to a label's grid: [link](http://stackoverflow.com/a/276868) – OfKo May 06 '16 at 13:34
  • If there are "internal" bindings in NewMazeGrid's XAML (i.e. to its own properties), then you might have to set `RelativeSource={RelativeSource AncestorType=UserControl}` on these bindings. – Clemens May 06 '16 at 13:47
  • I changed from Lists+propertyChanged to ObservableCollection and now it works! Thank you so much! :D – OfKo May 06 '16 at 14:21