Michael Pidde => { Programmer(); }

WPF Notes

Over a year ago I had started creating a generic map editor for my games. It was browser based using plain old Javascript to build the UI and control everything. I won't try to fool myself into thinking that I love JS, though I'm well-versed in it. Ultimately I wanted to move away from the browser and build a desktop program for a more streamlined and native experience. I decided to use WPF since I have some recent experience with it and, while my game is being written in C++, I'm having difficulty motivating myself to learn one of the GUI frameworks in that sphere. It's pretty darn easy to throw together a streamlined program with Visual Studio, and C# is an excellent language. Far more excellent syntactically than C++, that's for sure. Anyhow, here are a couple notes for myself about WPF particularities.

Finding Elements

When adding items to a container programmatically instead of in XAML, there can be problems using

(Type)container.FindName("name")
to access a child element. An alternative approach is to use
(Type)LogicalTreeHelper.FindLogicalNode(container, "name")

Configuring Bindings

Bindings are useful but verbose to set up. Programmatically, binding the visibility of a Rectangle will require the following:

  1. Implement INotifyPropertyChanged interface:
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string info) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
    }
  2. Create a property to use as a binding source:
    bool layer1Showing = true;
    public bool Layer1Showing {
        get {
            return layer1Showing;
        }
        set {
            layer1Showing = value;
            NotifyPropertyChanged("Layer1Showing");
        }
    }
  3. Set up binding and associate it to your element:
    Binding binding = new Binding("Layer1Showing") {
        Mode = BindingMode.OneWay,
        Converter = new BooleanToVisibilityConverter(),
    };
    
    rect.SetBinding(Rectangle.VisibilityProperty, binding);
  4. Set event handler to set property:
    private void ShowLayer1_Click(object sender, RoutedEventArgs e) {
        Layer1Showing = !Layer1Showing;
    }

Fill Rectangle with Portion of Bitmap

Because this is a 2D map editor, we want to be able to take a segment of a tile map palette image and "draw" that segment to our map which is represented by a 2-dimensional array of Rectangle objects. This can be done by creating a CroppedBitmap and setting the Rectangle's Fill property with it:

const int TILE_SIZE = 16;

Rectangle rect = new Rectangle() { ... }

// This prevents antialiasing
RenderOptions.SetBitmapScalingMode(rect, BitmapScalingMode.NearestNeighbor);

// This is the ultimate image that we'll draw with. It's source gets set below after creating the CroppedImage
Image cropped = new Image() {
    Width = TILE_SIZE,
    Height = TILE_SIZE,
};

// This is the palette image that we select a "tile" from
BitmapImage img = new BitmapImage();
img.BeginInit();
img.UriSource = new Uri(@"path/to/image.png");
img.EndInit();

// col and row are set prior corresponding to the grid position of the palette image segment that we want to draw with
CroppedBitmap cb = new CroppedBitmap(img, new Int32Rect(
    col * TILE_SIZE, 
    row * TILE_SIZE, 
    TILE_SIZE, 
    TILE_SIZE)
);
cropped.Source = cb;

// Finally, we set the background of the Rectangle with our image segment
rect.Fill = new ImageBrush(cb);