/// <summary> /// Creation of an expression to manage modulo (positive and negative value) /// </summary> /// <param name="scrollViewer">The ScrollViewer to synchronized. A null value is valid</param> /// <param name="imageWidth">Width of the image</param> /// <param name="imageHeight">Height of the image</param> /// <param name="scrollOrientation">The ScrollOrientation</param> private void CreateModuloExpression(ScrollViewer scrollViewer, double imageWidth, double imageHeight, ScrollOrientation scrollOrientation) { const string offsetXParam = "offsetX"; const string offsetYParam = "offsetY"; const string imageWidthParam = "imageWidth"; const string imageHeightParam = "imageHeight"; const string speedParam = "speed"; if (_containerVisual == null) { return; } var compositor = _containerVisual.Compositor; // Setup the expression ExpressionNode expressionX = null; ExpressionNode expressionY = null; ExpressionNode expressionXVal; ExpressionNode expressionYVal; var propertySetModulo = compositor.CreatePropertySet(); propertySetModulo.InsertScalar(imageWidthParam, (float)imageWidth); propertySetModulo.InsertScalar(offsetXParam, (float)OffsetX); propertySetModulo.InsertScalar(imageHeightParam, (float)imageHeight); propertySetModulo.InsertScalar(offsetYParam, (float)OffsetY); propertySetModulo.InsertScalar(speedParam, (float)ParallaxSpeedRatio); var propertySetNodeModulo = propertySetModulo.GetReference(); var imageHeightNode = propertySetNodeModulo.GetScalarProperty(imageHeightParam); var imageWidthNode = propertySetNodeModulo.GetScalarProperty(imageWidthParam); if (scrollViewer == null) { var offsetXNode = ExpressionFunctions.Ceil(propertySetNodeModulo.GetScalarProperty(offsetXParam)); var offsetYNode = ExpressionFunctions.Ceil(propertySetNodeModulo.GetScalarProperty(offsetYParam)); // expressions are created to simulate a positive and negative modulo with the size of the image and the offset expressionXVal = ExpressionFunctions.Conditional( offsetXNode == 0, 0, ExpressionFunctions.Conditional( offsetXNode < 0, -(ExpressionFunctions.Abs(offsetXNode - (ExpressionFunctions.Ceil(offsetXNode / imageWidthNode) * imageWidthNode)) % imageWidthNode), -(imageWidthNode - (offsetXNode % imageWidthNode)))); expressionYVal = ExpressionFunctions.Conditional( offsetYNode == 0, 0, ExpressionFunctions.Conditional( offsetYNode < 0, -(ExpressionFunctions.Abs(offsetYNode - (ExpressionFunctions.Ceil(offsetYNode / imageHeightNode) * imageHeightNode)) % imageHeightNode), -(imageHeightNode - (offsetYNode % imageHeightNode)))); } else { // expressions are created to simulate a positive and negative modulo with the size of the image and the offset and the ScrollViewer offset (Translation) var scrollProperties = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scrollViewer); var scrollPropSet = scrollProperties.GetSpecializedReference <ManipulationPropertySetReferenceNode>(); var speed = propertySetNodeModulo.GetScalarProperty(speedParam); var xCommon = ExpressionFunctions.Ceil((scrollPropSet.Translation.X * speed) + propertySetNodeModulo.GetScalarProperty(offsetXParam)); expressionXVal = ExpressionFunctions.Conditional( xCommon == 0, 0, ExpressionFunctions.Conditional( xCommon < 0, -(ExpressionFunctions.Abs(xCommon - (ExpressionFunctions.Ceil(xCommon / imageWidthNode) * imageWidthNode)) % imageWidthNode), -(imageWidthNode - (xCommon % imageWidthNode)))); var yCommon = ExpressionFunctions.Ceil((scrollPropSet.Translation.Y * speed) + propertySetNodeModulo.GetScalarProperty(offsetYParam)); expressionYVal = ExpressionFunctions.Conditional( yCommon == 0, 0, ExpressionFunctions.Conditional( yCommon < 0, -(ExpressionFunctions.Abs(yCommon - (ExpressionFunctions.Ceil(yCommon / imageHeightNode) * imageHeightNode)) % imageHeightNode), -(imageHeightNode - (yCommon % imageHeightNode)))); } if (scrollOrientation == ScrollOrientation.Horizontal || scrollOrientation == ScrollOrientation.Both) { expressionX = expressionXVal; if (scrollOrientation == ScrollOrientation.Horizontal) { // In horizontal mode we never move the offset y expressionY = (ScalarNode)0.0f; _containerVisual.Offset = new Vector3((float)OffsetY, 0, 0); } } if (scrollOrientation == ScrollOrientation.Vertical || scrollOrientation == ScrollOrientation.Both) { expressionY = expressionYVal; if (scrollOrientation == ScrollOrientation.Vertical) { // In vertical mode we never move the offset x expressionX = (ScalarNode)0.0f; _containerVisual.Offset = new Vector3(0, (float)OffsetX, 0); } } _containerVisual.StopAnimation("Offset.X"); _containerVisual.StopAnimation("Offset.Y"); _containerVisual.StartAnimation("Offset.X", expressionX); _containerVisual.StartAnimation("Offset.Y", expressionY); _propertySetModulo = propertySetModulo; }